feat: mac-agent with AppleScript dispatch and unit tests

main
boksbc 2026-05-11 21:55:03 +02:00
parent ac19753ace
commit c470d28f59
2 changed files with 123 additions and 0 deletions

View File

@ -0,0 +1,49 @@
const { resolveCommand } = require('../electron/mac-agent');
describe('resolveCommand', () => {
test('bekannter Shortcut → applescript', () => {
expect(resolveCommand('spotify_play')).toEqual({
type: 'applescript',
script: 'tell application "Spotify" to play',
});
});
test('script: Präfix → applescript mit direktem Script', () => {
expect(resolveCommand('script:tell application "Finder" to activate')).toEqual({
type: 'applescript',
script: 'tell application "Finder" to activate',
});
});
test('open: Präfix → URL öffnen', () => {
expect(resolveCommand('open:https://example.com')).toEqual({
type: 'open',
url: 'https://example.com',
});
});
test('app: Präfix → App öffnen', () => {
expect(resolveCommand('app:Spotify')).toEqual({
type: 'app',
name: 'Spotify',
});
});
test('save: Präfix → Datei auf Desktop speichern', () => {
expect(resolveCommand('save:{"filename":"test.txt","content":"hallo"}')).toEqual({
type: 'save',
filename: 'test.txt',
content: 'hallo',
});
});
test('unbekannter Befehl → unknown', () => {
expect(resolveCommand('gibts_nicht')).toEqual({ type: 'unknown' });
});
test('volume_up ist bekannter Shortcut', () => {
const result = resolveCommand('volume_up');
expect(result.type).toBe('applescript');
expect(result.script).toContain('output volume');
});
});

74
electron/mac-agent.js Normal file
View File

@ -0,0 +1,74 @@
const { exec, execSync } = require('child_process');
const util = require('util');
const fs = require('fs');
const execAsync = util.promisify(exec);
const SCRIPTS = {
spotify_play: 'tell application "Spotify" to play',
spotify_pause: 'tell application "Spotify" to pause',
spotify_next: 'tell application "Spotify" to next track',
spotify_prev: 'tell application "Spotify" to previous track',
music_play: 'tell application "Music" to play',
music_pause: 'tell application "Music" to pause',
music_next: 'tell application "Music" to next track',
mail_open: 'tell application "Mail" to activate',
mail_unread: 'tell application "Mail" to return (count of (messages of inbox whose read status is false))',
calendar_open: 'tell application "Calendar" to activate',
notes_open: 'tell application "Notes" to activate',
volume_up: 'set volume output volume (output volume of (get volume settings) + 10)',
volume_down: 'set volume output volume (output volume of (get volume settings) - 10)',
volume_mute: 'set volume output muted true',
volume_unmute: 'set volume output muted false',
dark_mode_on: 'tell app "System Events" to tell appearance preferences to set dark mode to true',
dark_mode_off: 'tell app "System Events" to tell appearance preferences to set dark mode to false',
screenshot: 'do shell script "screencapture -x ~/Desktop/fox-screenshot-$(date +%Y%m%d-%H%M%S).png"',
};
function resolveCommand(command) {
if (SCRIPTS[command]) {
return { type: 'applescript', script: SCRIPTS[command] };
}
if (command.startsWith('script:')) {
return { type: 'applescript', script: command.slice(7) };
}
if (command.startsWith('open:')) {
return { type: 'open', url: command.slice(5) };
}
if (command.startsWith('app:')) {
return { type: 'app', name: command.slice(4) };
}
if (command.startsWith('save:')) {
return { type: 'save', ...JSON.parse(command.slice(5)) };
}
return { type: 'unknown' };
}
async function executeMacCommand(command) {
try {
const resolved = resolveCommand(command);
if (resolved.type === 'applescript') {
const { stdout } = await execAsync(`osascript -e '${resolved.script}'`);
return { success: true, output: stdout.trim() };
}
if (resolved.type === 'open') {
execSync(`open "${resolved.url}"`);
return { success: true, output: 'Geöffnet' };
}
if (resolved.type === 'app') {
execSync(`open -a "${resolved.name}"`);
return { success: true, output: `${resolved.name} geöffnet` };
}
if (resolved.type === 'save') {
const filePath = `${process.env.HOME}/Desktop/${resolved.filename}`;
fs.writeFileSync(filePath, resolved.content, 'utf8');
return { success: true, output: `Gespeichert: ${filePath}` };
}
return { success: false, output: 'Unbekannter Befehl' };
} catch (err) {
return { success: false, output: err.message };
}
}
module.exports = { executeMacCommand, resolveCommand, SCRIPTS };