aiden-runtime 3.16.2 → 3.18.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/README.md +185 -7
- package/config/devos.config.json +29 -19
- package/config/hardware.json +2 -2
- package/dist/api/dashboard.js +480 -0
- package/dist/api/server.js +150 -142
- package/dist/core/agentLoop.js +94 -13
- package/dist/core/channels/email.js +1 -1
- package/dist/core/modelRegistry.js +261 -0
- package/dist/core/permissionSystem.js +239 -0
- package/dist/core/pluginLoader.js +161 -0
- package/dist/core/skillLoader.js +6 -24
- package/dist/core/toolRegistry.js +316 -31
- package/dist/core/version.js +1 -1
- package/dist/providers/router.js +2 -1
- package/dist-bundle/cli.js +50946 -29225
- package/dist-bundle/index.js +6462 -5274
- package/package.json +3 -2
|
@@ -67,6 +67,7 @@ const mcpClient_1 = require("./mcpClient");
|
|
|
67
67
|
const codeInterpreter_1 = require("./codeInterpreter");
|
|
68
68
|
const sandboxRunner_1 = require("./sandboxRunner");
|
|
69
69
|
const responseCache_1 = require("./responseCache");
|
|
70
|
+
const permissionSystem_1 = require("./permissionSystem");
|
|
70
71
|
const youtubeTranscript_1 = require("./youtubeTranscript");
|
|
71
72
|
const knowledgeBase_1 = require("./knowledgeBase");
|
|
72
73
|
const calendarTool_1 = require("./tools/calendarTool");
|
|
@@ -182,6 +183,16 @@ const SHELL_ALLOWLIST = [
|
|
|
182
183
|
// 13. Instant Actions: lock screen (rundll32) and volume one-liners (powershell -c)
|
|
183
184
|
/^rundll32\b/i,
|
|
184
185
|
/^powershell\s+-c\b/i,
|
|
186
|
+
// 14. Process / app control — close named apps by name (user-directed, not destructive)
|
|
187
|
+
/^taskkill\s+\/im\s+\S+/i, // taskkill /im chrome.exe
|
|
188
|
+
/^taskkill\s+\/f\s+\/im\s+\S+/i, // taskkill /f /im chrome.exe
|
|
189
|
+
/^Stop-Process\s+-Name\b/i, // Stop-Process -Name chrome
|
|
190
|
+
/^Stop-Process\s+-Id\b/i, // Stop-Process -Id 1234
|
|
191
|
+
/^kill\b/i, // kill <pid> (Unix/WSL)
|
|
192
|
+
// 15. Volume / brightness / display controls
|
|
193
|
+
/^nircmd\b/i,
|
|
194
|
+
// 16. Windows window management
|
|
195
|
+
/^(start|explorer)\b/i,
|
|
185
196
|
];
|
|
186
197
|
function isCommandAllowed(cmd) {
|
|
187
198
|
// Hard-block: denylist and dangerous patterns take priority
|
|
@@ -284,6 +295,7 @@ const TOOL_TIMEOUTS = {
|
|
|
284
295
|
window_focus: 8000,
|
|
285
296
|
app_launch: 10000,
|
|
286
297
|
app_close: 8000,
|
|
298
|
+
system_volume: 8000,
|
|
287
299
|
watch_folder: 10000,
|
|
288
300
|
watch_folder_list: 5000,
|
|
289
301
|
clarify: 300000, // up to 5 min for human response
|
|
@@ -331,9 +343,32 @@ exports.TOOLS = {
|
|
|
331
343
|
const url = p.url || p.command || '';
|
|
332
344
|
if (!url)
|
|
333
345
|
return { success: false, output: '', error: 'No URL provided' };
|
|
346
|
+
// ── Permission system check ────────────────────────────────
|
|
347
|
+
const permBrowser = permissionSystem_1.permissionSystem.checkBrowserDomain(url);
|
|
348
|
+
if (permBrowser.verdict === 'deny') {
|
|
349
|
+
console.warn(`[Permissions] open_browser DENIED: ${url}`);
|
|
350
|
+
return { success: false, output: '', error: permBrowser.reason || 'Blocked by permission system.' };
|
|
351
|
+
}
|
|
352
|
+
if (permBrowser.verdict === 'ask') {
|
|
353
|
+
return { success: false, output: '', error: `PermissionGate: Navigation to this URL requires explicit user approval: ${url}` };
|
|
354
|
+
}
|
|
334
355
|
const r = await (0, playwrightBridge_1.pwNavigate)(url);
|
|
335
|
-
if (r.ok)
|
|
356
|
+
if (r.ok) {
|
|
357
|
+
// Auto-chain: if we landed on a YouTube search results page, immediately
|
|
358
|
+
// click the first video — so "play X on YouTube" works in a single step
|
|
359
|
+
// even when the planner forgets to emit the browser_click follow-up.
|
|
360
|
+
if (r.url.includes('youtube.com/results')) {
|
|
361
|
+
console.log('[open_browser] YouTube search detected — auto-clicking first result');
|
|
362
|
+
const click = await (0, playwrightBridge_1.pwClickFirstResult)();
|
|
363
|
+
if (click.ok) {
|
|
364
|
+
return { success: true, output: `Opened YouTube → playing first result → ${click.url ?? r.url}` };
|
|
365
|
+
}
|
|
366
|
+
console.warn(`[open_browser] YouTube auto-click failed: ${click.error}`);
|
|
367
|
+
// Navigation still succeeded; report it and let a browser_click retry handle it
|
|
368
|
+
return { success: true, output: `Opened browser: ${r.url} (auto-click failed: ${click.error})` };
|
|
369
|
+
}
|
|
336
370
|
return { success: true, output: `Opened browser: ${r.url}` };
|
|
371
|
+
}
|
|
337
372
|
// Playwright failed — fall back to system browser open
|
|
338
373
|
// (Legacy path: activeBrowserPage = null; openBrowser(url))
|
|
339
374
|
try {
|
|
@@ -485,14 +520,29 @@ exports.TOOLS = {
|
|
|
485
520
|
const cmd = p.command || p.cmd || '';
|
|
486
521
|
if (!cmd)
|
|
487
522
|
return { success: false, output: '', error: 'No command' };
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
523
|
+
// ── Permission system check (workspace/permissions.yaml) ──
|
|
524
|
+
const permCheck = permissionSystem_1.permissionSystem.checkShell(cmd);
|
|
525
|
+
if (permCheck.verdict === 'deny') {
|
|
526
|
+
console.warn(`[Permissions] shell_exec DENIED: ${cmd.slice(0, 120)}`);
|
|
527
|
+
return { success: false, output: '', error: permCheck.reason || 'Blocked by permission system.' };
|
|
528
|
+
}
|
|
529
|
+
if (permCheck.verdict === 'ask') {
|
|
530
|
+
console.warn(`[Permissions] shell_exec ASK — approval required: ${cmd.slice(0, 120)}`);
|
|
531
|
+
return { success: false, output: '', error: `PermissionGate: This command requires explicit user approval: ${cmd.slice(0, 80)}` };
|
|
532
|
+
}
|
|
533
|
+
// verdict === 'allow' skips the hardcoded gate below
|
|
534
|
+
// verdict === 'defer' falls through to the existing SHELL_ALLOWLIST gate
|
|
535
|
+
if (permCheck.verdict !== 'allow') {
|
|
536
|
+
// Existing hardcoded gate (DENIED_COMMANDS + SHELL_DANGEROUS + SHELL_ALLOWLIST)
|
|
537
|
+
const shellGate = isCommandAllowed(cmd);
|
|
538
|
+
if (!shellGate.allowed) {
|
|
539
|
+
if (shellGate.needsApproval) {
|
|
540
|
+
console.warn(`[AllowList] shell_exec UNKNOWN — approval required: ${cmd.slice(0, 120)}`);
|
|
541
|
+
return { success: false, output: '', error: `CommandGate: This command requires explicit user approval before running: ${cmd.slice(0, 80)}` };
|
|
542
|
+
}
|
|
543
|
+
console.warn(`[Security] shell_exec DENIED: ${cmd.slice(0, 120)}`);
|
|
544
|
+
return { success: false, output: '', error: 'Blocked: this command pattern is not allowed. Dangerous operations require explicit user approval.' };
|
|
493
545
|
}
|
|
494
|
-
console.warn(`[Security] shell_exec DENIED: ${cmd.slice(0, 120)}`);
|
|
495
|
-
return { success: false, output: '', error: 'Blocked: this command pattern is not allowed. Dangerous operations require explicit user approval.' };
|
|
496
546
|
}
|
|
497
547
|
// ── N+34: Docker sandbox routing ───────────────────────────
|
|
498
548
|
const _sandboxMode = process.env.AIDEN_SANDBOX_MODE || 'off';
|
|
@@ -552,14 +602,25 @@ exports.TOOLS = {
|
|
|
552
602
|
const script = p.script || p.command || '';
|
|
553
603
|
if (!script)
|
|
554
604
|
return { success: false, output: '', error: 'No script' };
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
605
|
+
// ── Permission system check ────────────────────────────────
|
|
606
|
+
const permPs = permissionSystem_1.permissionSystem.checkShell(script);
|
|
607
|
+
if (permPs.verdict === 'deny') {
|
|
608
|
+
console.warn(`[Permissions] run_powershell DENIED: ${script.slice(0, 120)}`);
|
|
609
|
+
return { success: false, output: '', error: permPs.reason || 'Blocked by permission system.' };
|
|
610
|
+
}
|
|
611
|
+
if (permPs.verdict === 'ask') {
|
|
612
|
+
return { success: false, output: '', error: `PermissionGate: This PowerShell command requires explicit user approval: ${script.slice(0, 80)}` };
|
|
613
|
+
}
|
|
614
|
+
if (permPs.verdict !== 'allow') {
|
|
615
|
+
const psGate = isCommandAllowed(script);
|
|
616
|
+
if (!psGate.allowed) {
|
|
617
|
+
if (psGate.needsApproval) {
|
|
618
|
+
console.warn(`[AllowList] run_powershell UNKNOWN — approval required: ${script.slice(0, 120)}`);
|
|
619
|
+
return { success: false, output: '', error: `CommandGate: This PowerShell command requires explicit user approval before running.` };
|
|
620
|
+
}
|
|
621
|
+
console.warn(`[Security] run_powershell DENIED: ${script.slice(0, 120)}`);
|
|
622
|
+
return { success: false, output: '', error: 'Blocked: this command pattern is not allowed. Dangerous operations require explicit user approval.' };
|
|
560
623
|
}
|
|
561
|
-
console.warn(`[Security] run_powershell DENIED: ${script.slice(0, 120)}`);
|
|
562
|
-
return { success: false, output: '', error: 'Blocked: this command pattern is not allowed. Dangerous operations require explicit user approval.' };
|
|
563
624
|
}
|
|
564
625
|
const tmpFile = path_1.default.join(process.cwd(), 'workspace', `tmp_${Date.now()}.ps1`);
|
|
565
626
|
fs_1.default.mkdirSync(path_1.default.dirname(tmpFile), { recursive: true });
|
|
@@ -664,6 +725,12 @@ exports.TOOLS = {
|
|
|
664
725
|
const content = p.content || '';
|
|
665
726
|
if (!filePath)
|
|
666
727
|
return { success: false, output: '', error: 'No path' };
|
|
728
|
+
// ── Permission system check ────────────────────────────────
|
|
729
|
+
const permWrite = permissionSystem_1.permissionSystem.checkFileWrite(filePath);
|
|
730
|
+
if (permWrite.verdict === 'deny') {
|
|
731
|
+
console.warn(`[Permissions] file_write DENIED: ${filePath}`);
|
|
732
|
+
return { success: false, output: '', error: permWrite.reason || 'Blocked by permission system.' };
|
|
733
|
+
}
|
|
667
734
|
if (isProtectedFile(filePath)) {
|
|
668
735
|
console.warn(`[Security] file_write BLOCKED (protected): ${filePath}`);
|
|
669
736
|
return { success: false, output: '', error: `Protected file: ${filePath} cannot be modified by agents. Use 'devos config' or edit manually.` };
|
|
@@ -702,6 +769,12 @@ exports.TOOLS = {
|
|
|
702
769
|
let filePath = p.path || p.file || '';
|
|
703
770
|
if (!filePath)
|
|
704
771
|
return { success: false, output: '', error: 'No path' };
|
|
772
|
+
// ── Permission system check ────────────────────────────────
|
|
773
|
+
const permRead = permissionSystem_1.permissionSystem.checkFileRead(filePath);
|
|
774
|
+
if (permRead.verdict === 'deny') {
|
|
775
|
+
console.warn(`[Permissions] file_read DENIED: ${filePath}`);
|
|
776
|
+
return { success: false, output: '', error: permRead.reason || 'Blocked by permission system.' };
|
|
777
|
+
}
|
|
705
778
|
if (isPathDenied(filePath)) {
|
|
706
779
|
console.warn(`[Security] file_read DENIED: ${filePath}`);
|
|
707
780
|
return { success: false, output: '', error: 'Access denied: protected path. Aiden cannot read credentials, SSH keys, or env files.' };
|
|
@@ -1526,31 +1599,241 @@ exports.TOOLS = {
|
|
|
1526
1599
|
}
|
|
1527
1600
|
},
|
|
1528
1601
|
app_launch: async (p) => {
|
|
1529
|
-
const
|
|
1530
|
-
if (!
|
|
1531
|
-
return { success: false, output: '', error: 'No
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1602
|
+
const appName = (p.app_name ?? p.appName ?? p.app ?? p.path ?? p.command ?? p.name ?? p.target ?? '').toString().toLowerCase().trim();
|
|
1603
|
+
if (!appName)
|
|
1604
|
+
return { success: false, output: '', error: 'No app_name provided. Pass app_name e.g. "chrome" or "spotify".' };
|
|
1605
|
+
// Map friendly display names → executable/URI names
|
|
1606
|
+
const exeMap = {
|
|
1607
|
+
'chrome': 'chrome',
|
|
1608
|
+
'google chrome': 'chrome',
|
|
1609
|
+
'firefox': 'firefox',
|
|
1610
|
+
'edge': 'msedge',
|
|
1611
|
+
'microsoft edge': 'msedge',
|
|
1612
|
+
'spotify': 'spotify',
|
|
1613
|
+
'discord': 'discord',
|
|
1614
|
+
'vscode': 'code',
|
|
1615
|
+
'vs code': 'code',
|
|
1616
|
+
'visual studio code': 'code',
|
|
1617
|
+
'notepad': 'notepad',
|
|
1618
|
+
'notepad++': 'notepad++',
|
|
1619
|
+
'word': 'winword',
|
|
1620
|
+
'excel': 'excel',
|
|
1621
|
+
'powerpoint': 'powerpnt',
|
|
1622
|
+
'slack': 'slack',
|
|
1623
|
+
'zoom': 'zoom',
|
|
1624
|
+
'teams': 'teams',
|
|
1625
|
+
'microsoft teams': 'teams',
|
|
1626
|
+
'explorer': 'explorer',
|
|
1627
|
+
'file explorer': 'explorer',
|
|
1628
|
+
'task manager': 'taskmgr',
|
|
1629
|
+
'taskmgr': 'taskmgr',
|
|
1630
|
+
'calculator': 'calc',
|
|
1631
|
+
'calc': 'calc',
|
|
1632
|
+
'paint': 'mspaint',
|
|
1633
|
+
'terminal': 'wt',
|
|
1634
|
+
'windows terminal': 'wt',
|
|
1635
|
+
'cmd': 'cmd',
|
|
1636
|
+
'powershell': 'powershell',
|
|
1637
|
+
};
|
|
1638
|
+
const exe = exeMap[appName] ?? appName;
|
|
1535
1639
|
try {
|
|
1536
1640
|
const { execSync } = await Promise.resolve().then(() => __importStar(require('child_process')));
|
|
1537
|
-
|
|
1538
|
-
execSync(`
|
|
1539
|
-
return { success: true, output: `Launched: "${
|
|
1641
|
+
// Use cmd /c start — cmd built-in, avoids Start-Process (blocked by permissions)
|
|
1642
|
+
execSync(`cmd /c start "" "${exe}"`, { timeout: 10000 });
|
|
1643
|
+
return { success: true, output: `Launched: "${appName}"` };
|
|
1540
1644
|
}
|
|
1541
1645
|
catch (e) {
|
|
1542
1646
|
return { success: false, output: '', error: e.message };
|
|
1543
1647
|
}
|
|
1544
1648
|
},
|
|
1545
1649
|
app_close: async (p) => {
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1650
|
+
// Accept app_name (planner default), appName, name, app, process, command
|
|
1651
|
+
const appName = (p.app_name ?? p.appName ?? p.app ?? p.process ?? p.command ?? p.name ?? p.target ?? '').toString().toLowerCase().trim();
|
|
1652
|
+
if (!appName)
|
|
1653
|
+
return { success: false, output: '', error: 'No app name provided. Pass app_name e.g. "chrome" or "spotify".' };
|
|
1654
|
+
const exeMap = {
|
|
1655
|
+
'chrome': 'chrome.exe',
|
|
1656
|
+
'google chrome': 'chrome.exe',
|
|
1657
|
+
'chrome browser': 'chrome.exe',
|
|
1658
|
+
'firefox': 'firefox.exe',
|
|
1659
|
+
'mozilla firefox': 'firefox.exe',
|
|
1660
|
+
'edge': 'msedge.exe',
|
|
1661
|
+
'microsoft edge': 'msedge.exe',
|
|
1662
|
+
'spotify': 'Spotify.exe',
|
|
1663
|
+
'notepad': 'notepad.exe',
|
|
1664
|
+
'notepad++': 'notepad++.exe',
|
|
1665
|
+
'word': 'WINWORD.EXE',
|
|
1666
|
+
'microsoft word': 'WINWORD.EXE',
|
|
1667
|
+
'excel': 'EXCEL.EXE',
|
|
1668
|
+
'microsoft excel': 'EXCEL.EXE',
|
|
1669
|
+
'powerpoint': 'POWERPNT.EXE',
|
|
1670
|
+
'microsoft powerpoint': 'POWERPNT.EXE',
|
|
1671
|
+
'vscode': 'Code.exe',
|
|
1672
|
+
'vs code': 'Code.exe',
|
|
1673
|
+
'visual studio code': 'Code.exe',
|
|
1674
|
+
'discord': 'Discord.exe',
|
|
1675
|
+
'slack': 'slack.exe',
|
|
1676
|
+
'zoom': 'Zoom.exe',
|
|
1677
|
+
'teams': 'Teams.exe',
|
|
1678
|
+
'microsoft teams': 'Teams.exe',
|
|
1679
|
+
'vlc': 'vlc.exe',
|
|
1680
|
+
'steam': 'steam.exe',
|
|
1681
|
+
'explorer': 'explorer.exe',
|
|
1682
|
+
'file explorer': 'explorer.exe',
|
|
1683
|
+
'windows explorer': 'explorer.exe',
|
|
1684
|
+
'cmd': 'cmd.exe',
|
|
1685
|
+
'command prompt': 'cmd.exe',
|
|
1686
|
+
'terminal': 'wt.exe',
|
|
1687
|
+
'windows terminal': 'wt.exe',
|
|
1688
|
+
'paint': 'mspaint.exe',
|
|
1689
|
+
'ms paint': 'mspaint.exe',
|
|
1690
|
+
'calculator': 'Calculator.exe',
|
|
1691
|
+
'task manager': 'Taskmgr.exe',
|
|
1692
|
+
'taskmgr': 'Taskmgr.exe',
|
|
1693
|
+
'whatsapp': 'WhatsApp.exe',
|
|
1694
|
+
'telegram': 'Telegram.exe',
|
|
1695
|
+
'obs': 'obs64.exe',
|
|
1696
|
+
'obs studio': 'obs64.exe',
|
|
1697
|
+
'brave': 'brave.exe',
|
|
1698
|
+
'brave browser': 'brave.exe',
|
|
1699
|
+
'opera': 'opera.exe',
|
|
1700
|
+
'winamp': 'winamp.exe',
|
|
1701
|
+
'itunes': 'iTunes.exe',
|
|
1702
|
+
};
|
|
1703
|
+
const exe = exeMap[appName] ?? (appName.endsWith('.exe') ? appName : appName + '.exe');
|
|
1704
|
+
try {
|
|
1705
|
+
const { execSync } = await Promise.resolve().then(() => __importStar(require('child_process')));
|
|
1706
|
+
execSync(`taskkill /F /IM "${exe}"`, { timeout: 5000 });
|
|
1707
|
+
return { success: true, output: `Closed: "${appName}" (${exe})` };
|
|
1708
|
+
}
|
|
1709
|
+
catch (e) {
|
|
1710
|
+
const msg = (e.message || '').toLowerCase();
|
|
1711
|
+
if (msg.includes('not found') || msg.includes('no tasks')) {
|
|
1712
|
+
return { success: false, output: '', error: `Process not found: ${exe} — is "${appName}" running?` };
|
|
1713
|
+
}
|
|
1714
|
+
return { success: false, output: '', error: e.message };
|
|
1715
|
+
}
|
|
1716
|
+
},
|
|
1717
|
+
system_volume: async (p) => {
|
|
1718
|
+
// ── Natural input detection ──────────────────────────────────
|
|
1719
|
+
// Planner may send { volume: 20 }, { level: 50 }, { mute: true },
|
|
1720
|
+
// { action: "up", amount: 20 }, or a mix — normalise all forms here.
|
|
1721
|
+
let action = (p.action ?? '').toString().toLowerCase().trim();
|
|
1722
|
+
let amount = Number(p.amount ?? p.percent ?? p.by ?? 0);
|
|
1723
|
+
let target = p.level !== undefined ? Number(p.level)
|
|
1724
|
+
: p.set !== undefined ? Number(p.set) : -1;
|
|
1725
|
+
if (!action) {
|
|
1726
|
+
if (p.mute === true)
|
|
1727
|
+
action = 'mute';
|
|
1728
|
+
else if (p.unmute === true)
|
|
1729
|
+
action = 'unmute';
|
|
1730
|
+
else if (typeof p.volume === 'number') {
|
|
1731
|
+
action = (p.direction === 'down') ? 'down' : 'up';
|
|
1732
|
+
amount = p.volume;
|
|
1733
|
+
}
|
|
1734
|
+
else if (typeof p.level === 'number')
|
|
1735
|
+
action = 'set';
|
|
1736
|
+
// { amount: 20 } or { by: 20 } with no other hints → default up
|
|
1737
|
+
else if (amount > 0)
|
|
1738
|
+
action = (p.direction === 'down') ? 'down' : 'up';
|
|
1739
|
+
else
|
|
1740
|
+
action = 'get';
|
|
1741
|
+
}
|
|
1742
|
+
// Sensible default step when caller didn't supply an amount
|
|
1743
|
+
if (!amount && action !== 'get' && action !== 'mute' && action !== 'unmute') {
|
|
1744
|
+
amount = 20;
|
|
1745
|
+
}
|
|
1549
1746
|
try {
|
|
1550
1747
|
const { execSync } = await Promise.resolve().then(() => __importStar(require('child_process')));
|
|
1551
|
-
const
|
|
1552
|
-
|
|
1553
|
-
|
|
1748
|
+
const { writeFileSync, unlinkSync } = await Promise.resolve().then(() => __importStar(require('fs')));
|
|
1749
|
+
const { tmpdir } = await Promise.resolve().then(() => __importStar(require('os')));
|
|
1750
|
+
const { join } = await Promise.resolve().then(() => __importStar(require('path')));
|
|
1751
|
+
// Helper: write + run + clean a temp .ps1 (avoids all quoting nightmares)
|
|
1752
|
+
const runPs = (script, label) => {
|
|
1753
|
+
const f = join(tmpdir(), `_aiden_${label}_${Date.now()}.ps1`);
|
|
1754
|
+
writeFileSync(f, script, 'utf8');
|
|
1755
|
+
try {
|
|
1756
|
+
return execSync(`powershell.exe -NoProfile -ExecutionPolicy Bypass -File "${f}"`, { timeout: 6000, encoding: 'utf8' }).trim();
|
|
1757
|
+
}
|
|
1758
|
+
finally {
|
|
1759
|
+
try {
|
|
1760
|
+
unlinkSync(f);
|
|
1761
|
+
}
|
|
1762
|
+
catch { }
|
|
1763
|
+
}
|
|
1764
|
+
};
|
|
1765
|
+
// Shared keybd_event helper — works without a focused window unlike WScript.Shell
|
|
1766
|
+
const keybdScript = (vk) => `
|
|
1767
|
+
Add-Type -TypeDefinition @"
|
|
1768
|
+
using System; using System.Runtime.InteropServices;
|
|
1769
|
+
public class AidenKbd {
|
|
1770
|
+
[DllImport("user32.dll")] public static extern void keybd_event(byte bVk, byte bScan, uint dwFlags, uint dwExtraInfo);
|
|
1771
|
+
}
|
|
1772
|
+
"@
|
|
1773
|
+
[AidenKbd]::keybd_event(${vk}, 0, 0, 0)
|
|
1774
|
+
Start-Sleep -Milliseconds 50
|
|
1775
|
+
[AidenKbd]::keybd_event(${vk}, 0, 2, 0)
|
|
1776
|
+
`;
|
|
1777
|
+
// ── Mute / Unmute — VK_VOLUME_MUTE = 0xAD = 173 ─────────
|
|
1778
|
+
if (action === 'mute' || action === 'unmute') {
|
|
1779
|
+
runPs(keybdScript(173), 'mute');
|
|
1780
|
+
return { success: true, output: action === 'mute' ? 'Muted' : 'Unmuted (toggle)' };
|
|
1781
|
+
}
|
|
1782
|
+
// ── Up / Down — fire VK_VOLUME_UP/DOWN N times ────────────
|
|
1783
|
+
// VK_VOLUME_DOWN = 0xAE = 174, VK_VOLUME_UP = 0xAF = 175
|
|
1784
|
+
if (action === 'up' || action === 'down') {
|
|
1785
|
+
const presses = Math.max(1, Math.round(amount / 2)); // each press ≈ 2%
|
|
1786
|
+
const vk = action === 'up' ? 175 : 174;
|
|
1787
|
+
const script = `
|
|
1788
|
+
Add-Type -TypeDefinition @"
|
|
1789
|
+
using System; using System.Runtime.InteropServices;
|
|
1790
|
+
public class AidenKbd2 {
|
|
1791
|
+
[DllImport("user32.dll")] public static extern void keybd_event(byte bVk, byte bScan, uint dwFlags, uint dwExtraInfo);
|
|
1792
|
+
}
|
|
1793
|
+
"@
|
|
1794
|
+
for ($i = 0; $i -lt ${presses}; $i++) {
|
|
1795
|
+
[AidenKbd2]::keybd_event(${vk}, 0, 0, 0)
|
|
1796
|
+
Start-Sleep -Milliseconds 30
|
|
1797
|
+
[AidenKbd2]::keybd_event(${vk}, 0, 2, 0)
|
|
1798
|
+
Start-Sleep -Milliseconds 20
|
|
1799
|
+
}
|
|
1800
|
+
`;
|
|
1801
|
+
runPs(script, action);
|
|
1802
|
+
return { success: true, output: `Volume ${action === 'up' ? 'increased' : 'decreased'} by ~${presses * 2}%` };
|
|
1803
|
+
}
|
|
1804
|
+
// ── Get — read waveOut scalar ─────────────────────────────
|
|
1805
|
+
if (action === 'get') {
|
|
1806
|
+
const raw = runPs(`
|
|
1807
|
+
Add-Type -TypeDefinition @"
|
|
1808
|
+
using System; using System.Runtime.InteropServices;
|
|
1809
|
+
public class AidenVolGet {
|
|
1810
|
+
[DllImport("winmm.dll")] public static extern int waveOutGetVolume(IntPtr h, out uint v);
|
|
1811
|
+
}
|
|
1812
|
+
"@
|
|
1813
|
+
$v = [uint32]0
|
|
1814
|
+
[AidenVolGet]::waveOutGetVolume([IntPtr]::Zero, [ref]$v) | Out-Null
|
|
1815
|
+
[Math]::Round(($v -band 0xFFFF) / 65535.0 * 100)
|
|
1816
|
+
`, 'get');
|
|
1817
|
+
const vol = Number(raw);
|
|
1818
|
+
return { success: true, output: `Current volume: ${vol}%`, volume: vol };
|
|
1819
|
+
}
|
|
1820
|
+
// ── Set — write exact waveOut scalar ─────────────────────
|
|
1821
|
+
if (action === 'set' && target >= 0) {
|
|
1822
|
+
const clamped = Math.max(0, Math.min(100, target));
|
|
1823
|
+
const scalar = Math.round(clamped / 100 * 65535);
|
|
1824
|
+
const dword = (scalar << 16) | scalar;
|
|
1825
|
+
runPs(`
|
|
1826
|
+
Add-Type -TypeDefinition @"
|
|
1827
|
+
using System; using System.Runtime.InteropServices;
|
|
1828
|
+
public class AidenVolSet {
|
|
1829
|
+
[DllImport("winmm.dll")] public static extern int waveOutSetVolume(IntPtr h, uint v);
|
|
1830
|
+
}
|
|
1831
|
+
"@
|
|
1832
|
+
[AidenVolSet]::waveOutSetVolume([IntPtr]::Zero, [uint32]${dword}) | Out-Null
|
|
1833
|
+
`, 'set');
|
|
1834
|
+
return { success: true, output: `Volume set to ${clamped}%`, volume: clamped };
|
|
1835
|
+
}
|
|
1836
|
+
return { success: false, output: '', error: `Unknown action: "${action}". Use: get, up, down, mute, unmute, set` };
|
|
1554
1837
|
}
|
|
1555
1838
|
catch (e) {
|
|
1556
1839
|
return { success: false, output: '', error: e.message };
|
|
@@ -2471,7 +2754,8 @@ exports.TOOL_DESCRIPTIONS = {
|
|
|
2471
2754
|
window_list: 'List all open windows on the desktop',
|
|
2472
2755
|
window_focus: 'Bring a specific window to the foreground by title',
|
|
2473
2756
|
app_launch: 'Launch an application by name or executable path',
|
|
2474
|
-
app_close: 'Close an application by window title',
|
|
2757
|
+
app_close: 'Close an application by window title or process name',
|
|
2758
|
+
system_volume: 'Get or set Windows speaker volume (get/up/down/mute/unmute/set)',
|
|
2475
2759
|
watch_folder: 'Watch a folder and react automatically when new files appear',
|
|
2476
2760
|
watch_folder_list: 'List all currently watched folder paths',
|
|
2477
2761
|
get_briefing: 'Run the morning briefing: weather, markets, news, and daily summary',
|
|
@@ -2565,6 +2849,7 @@ const TOOL_TIERS = {
|
|
|
2565
2849
|
window_focus: 3,
|
|
2566
2850
|
app_launch: 3,
|
|
2567
2851
|
app_close: 3,
|
|
2852
|
+
system_volume: 2,
|
|
2568
2853
|
// Voice tools — Tier 2 (subprocess / local model)
|
|
2569
2854
|
voice_speak: 2,
|
|
2570
2855
|
voice_transcribe: 2,
|
package/dist/core/version.js
CHANGED
package/dist/providers/router.js
CHANGED
|
@@ -32,6 +32,7 @@ const nvidia_1 = require("./nvidia");
|
|
|
32
32
|
const boa_1 = require("./boa");
|
|
33
33
|
const custom_1 = require("./custom");
|
|
34
34
|
const modelDiscovery_1 = require("../core/modelDiscovery");
|
|
35
|
+
const modelRegistry_1 = require("../core/modelRegistry");
|
|
35
36
|
// Per-provider rate-limit windows — tuned to actual reset characteristics.
|
|
36
37
|
// Previous flat 1-hour window was far too conservative for fast-reset APIs.
|
|
37
38
|
const RATE_LIMIT_WINDOWS = {
|
|
@@ -424,7 +425,7 @@ function getSmartProvider() {
|
|
|
424
425
|
// AUTO MODE: round-robin across available APIs
|
|
425
426
|
const next = getNextAvailableAPI();
|
|
426
427
|
if (next) {
|
|
427
|
-
return { provider: next.provider, model: next.entry.model || 'llama-3.3-70b-versatile', userName, apiName: next.entry.name };
|
|
428
|
+
return { provider: next.provider, model: next.entry.model || (0, modelRegistry_1.getDefaultModel)(next.entry.provider) || 'llama-3.3-70b-versatile', userName, apiName: next.entry.name };
|
|
428
429
|
}
|
|
429
430
|
// FALLBACK: best discovered Ollama model
|
|
430
431
|
if (config.routing?.fallbackToOllama !== false) {
|