From c470d28f594551f6b527910df2bff17c92753f58 Mon Sep 17 00:00:00 2001 From: boksbc Date: Mon, 11 May 2026 21:55:03 +0200 Subject: [PATCH] feat: mac-agent with AppleScript dispatch and unit tests --- __tests__/mac-agent.test.js | 49 ++++++++++++++++++++++++ electron/mac-agent.js | 74 +++++++++++++++++++++++++++++++++++++ 2 files changed, 123 insertions(+) create mode 100644 __tests__/mac-agent.test.js create mode 100644 electron/mac-agent.js diff --git a/__tests__/mac-agent.test.js b/__tests__/mac-agent.test.js new file mode 100644 index 0000000..6f76c7f --- /dev/null +++ b/__tests__/mac-agent.test.js @@ -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'); + }); +}); diff --git a/electron/mac-agent.js b/electron/mac-agent.js new file mode 100644 index 0000000..c591b60 --- /dev/null +++ b/electron/mac-agent.js @@ -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 };