sweetspot-remote-agent 1.5.0 → 1.7.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/lib/apps.mjs CHANGED
@@ -3,14 +3,56 @@ import os from "os";
3
3
 
4
4
  const platform = os.platform();
5
5
 
6
+ // 한국어/약칭 → macOS 앱 이름 매핑
7
+ const APP_ALIASES = {
8
+ "메모": "Notes", "메모장": "Notes", "notes": "Notes",
9
+ "사파리": "Safari", "safari": "Safari",
10
+ "크롬": "Google Chrome", "chrome": "Google Chrome", "구글크롬": "Google Chrome",
11
+ "파인더": "Finder", "finder": "Finder",
12
+ "터미널": "Terminal", "terminal": "Terminal",
13
+ "설정": "System Settings", "시스템설정": "System Settings",
14
+ "캘린더": "Calendar", "달력": "Calendar", "calendar": "Calendar",
15
+ "메일": "Mail", "mail": "Mail",
16
+ "메시지": "Messages", "messages": "Messages",
17
+ "음악": "Music", "music": "Music",
18
+ "사진": "Photos", "photos": "Photos",
19
+ "미리보기": "Preview", "preview": "Preview",
20
+ "계산기": "Calculator", "calculator": "Calculator",
21
+ "카카오톡": "KakaoTalk", "카톡": "KakaoTalk",
22
+ "슬랙": "Slack", "slack": "Slack",
23
+ "엑셀": "Microsoft Excel", "excel": "Microsoft Excel",
24
+ "워드": "Microsoft Word", "word": "Microsoft Word",
25
+ "파워포인트": "Microsoft PowerPoint", "ppt": "Microsoft PowerPoint",
26
+ "vscode": "Visual Studio Code", "코드": "Visual Studio Code",
27
+ "줌": "zoom.us", "zoom": "zoom.us",
28
+ "노션": "Notion", "notion": "Notion",
29
+ "피그마": "Figma", "figma": "Figma",
30
+ "텔레그램": "Telegram", "telegram": "Telegram",
31
+ };
32
+
33
+ function resolveAppName(appName) {
34
+ const lower = appName.toLowerCase().trim();
35
+ return APP_ALIASES[lower] || APP_ALIASES[appName] || appName;
36
+ }
37
+
6
38
  export function openApp(appName) {
39
+ const resolved = resolveAppName(appName);
7
40
  if (platform === "darwin") {
8
- try { execSync(`open -a "${appName}"`, { timeout: 5000 }); }
9
- catch { execSync(`mdfind "kMDItemKind == 'Application'" | grep -i "${appName}" | head -1 | xargs open`, { timeout: 5000 }); }
41
+ try {
42
+ execSync(`open -a "${resolved}"`, { timeout: 5000 });
43
+ } catch {
44
+ // Spotlight 검색으로 폴백
45
+ try {
46
+ execSync(`mdfind "kMDItemKind == 'Application'" | grep -i "${appName}" | head -1 | xargs open`, { timeout: 5000 });
47
+ } catch {
48
+ // AppleScript로 마지막 시도
49
+ execSync(`osascript -e 'tell application "${resolved}" to activate'`, { timeout: 5000 });
50
+ }
51
+ }
10
52
  } else if (platform === "win32") {
11
- execSync(`powershell -Command "Start-Process '${appName}'"`, { timeout: 5000 });
53
+ execSync(`powershell -Command "Start-Process '${resolved}'"`, { timeout: 5000 });
12
54
  } else {
13
- execSync(`${appName} &`, { timeout: 5000 });
55
+ execSync(`${resolved} &`, { timeout: 5000 });
14
56
  }
15
57
  }
16
58
 
@@ -31,15 +73,17 @@ export function listApps() {
31
73
  }
32
74
 
33
75
  export function closeApp(appName) {
34
- if (platform === "darwin") execSync(`osascript -e 'tell application "${appName}" to quit'`, { timeout: 5000 });
35
- else if (platform === "win32") execSync(`powershell -Command "Stop-Process -Name '${appName}' -Force"`, { timeout: 5000 });
36
- else execSync(`pkill -f "${appName}"`, { timeout: 5000 });
76
+ const resolved = resolveAppName(appName);
77
+ if (platform === "darwin") execSync(`osascript -e 'tell application "${resolved}" to quit'`, { timeout: 5000 });
78
+ else if (platform === "win32") execSync(`powershell -Command "Stop-Process -Name '${resolved}' -Force"`, { timeout: 5000 });
79
+ else execSync(`pkill -f "${resolved}"`, { timeout: 5000 });
37
80
  }
38
81
 
39
82
  export function focusApp(appName) {
40
- if (platform === "darwin") execSync(`osascript -e 'tell application "${appName}" to activate'`, { timeout: 5000 });
41
- else if (platform === "win32") execSync(`powershell -Command "(Get-Process '${appName}')[0].MainWindowHandle"`, { timeout: 5000 });
42
- else execSync(`xdotool search --name "${appName}" windowactivate`, { timeout: 5000 });
83
+ const resolved = resolveAppName(appName);
84
+ if (platform === "darwin") execSync(`osascript -e 'tell application "${resolved}" to activate'`, { timeout: 5000 });
85
+ else if (platform === "win32") execSync(`powershell -Command "(Get-Process '${resolved}')[0].MainWindowHandle"`, { timeout: 5000 });
86
+ else execSync(`xdotool search --name "${resolved}" windowactivate`, { timeout: 5000 });
43
87
  }
44
88
 
45
89
  export function getClipboard() {
@@ -49,7 +93,11 @@ export function getClipboard() {
49
93
  }
50
94
 
51
95
  export function setClipboard(text) {
52
- if (platform === "darwin") execSync(`echo "${text.replace(/"/g, '\\"')}" | pbcopy`, { timeout: 5000 });
53
- else if (platform === "win32") execSync(`powershell -Command "Set-Clipboard -Value '${text.replace(/'/g, "''")}'"`);
54
- else execSync(`echo "${text.replace(/"/g, '\\"')}" | xclip -selection clipboard`, { timeout: 5000 });
96
+ if (platform === "darwin") {
97
+ execSync("pbcopy", { input: text, timeout: 5000 });
98
+ } else if (platform === "win32") {
99
+ execSync(`powershell -Command "Set-Clipboard -Value '${text.replace(/'/g, "''")}'"`);
100
+ } else {
101
+ execSync("xclip -selection clipboard", { input: text, timeout: 5000 });
102
+ }
55
103
  }
package/lib/input.mjs CHANGED
@@ -3,6 +3,11 @@ import os from "os";
3
3
 
4
4
  const platform = os.platform();
5
5
 
6
+ // 텍스트에 비-ASCII 문자(한글 등)가 있는지 체크
7
+ function hasNonAscii(text) {
8
+ return /[^\x00-\x7F]/.test(text);
9
+ }
10
+
6
11
  export function click(x, y) {
7
12
  x = Math.round(x);
8
13
  y = Math.round(y);
@@ -34,11 +39,23 @@ export function doubleClick(x, y) {
34
39
 
35
40
  export function typeText(text) {
36
41
  if (platform === "darwin") {
37
- const escaped = text.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
38
- execSync(`osascript -e 'tell application "System Events" to keystroke "${escaped}"'`, { timeout: 10000 });
42
+ if (hasNonAscii(text)) {
43
+ // 한글 비-ASCII: 클립보드 경유 붙여넣기
44
+ execSync("pbcopy", { input: text, timeout: 5000 });
45
+ execSync(`osascript -e 'tell application "System Events" to keystroke "v" using command down'`, { timeout: 5000 });
46
+ } else {
47
+ const escaped = text.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
48
+ execSync(`osascript -e 'tell application "System Events" to keystroke "${escaped}"'`, { timeout: 10000 });
49
+ }
39
50
  } else if (platform === "win32") {
40
- const escaped = text.replace(/'/g, "''");
41
- execSync(`powershell -Command "Add-Type -AssemblyName System.Windows.Forms; [System.Windows.Forms.SendKeys]::SendWait('${escaped}')"`, { timeout: 10000 });
51
+ if (hasNonAscii(text)) {
52
+ // Windows: 클립보드 경유
53
+ execSync(`powershell -Command "Set-Clipboard -Value '${text.replace(/'/g, "''")}'"`);
54
+ execSync(`powershell -Command "Add-Type -AssemblyName System.Windows.Forms; [System.Windows.Forms.SendKeys]::SendWait('^v')"`, { timeout: 5000 });
55
+ } else {
56
+ const escaped = text.replace(/'/g, "''");
57
+ execSync(`powershell -Command "Add-Type -AssemblyName System.Windows.Forms; [System.Windows.Forms.SendKeys]::SendWait('${escaped}')"`, { timeout: 10000 });
58
+ }
42
59
  } else {
43
60
  execSync(`xdotool type -- "${text.replace(/"/g, '\\"')}"`, { timeout: 10000 });
44
61
  }
@@ -82,8 +99,13 @@ export function hotkey(keys) {
82
99
 
83
100
  export function scroll(direction, amount = 3) {
84
101
  if (platform === "darwin") {
85
- const delta = direction === "up" ? amount : -amount;
86
- execSync(`osascript -e 'tell application "System Events" to scroll ${delta}'`, { timeout: 5000 });
102
+ // cliclick scroll이 안정적
103
+ try {
104
+ execSync(`cliclick "scroll:0,${direction === "up" ? amount : -amount}"`, { timeout: 5000 });
105
+ } catch {
106
+ const delta = direction === "up" ? amount : -amount;
107
+ execSync(`osascript -e 'tell application "System Events" to scroll ${delta}'`, { timeout: 5000 });
108
+ }
87
109
  } else if (platform === "win32") {
88
110
  const keyDir = direction === "up" ? "{UP}" : "{DOWN}";
89
111
  for (let i = 0; i < amount; i++) {
package/mcp-server.js CHANGED
@@ -125,6 +125,21 @@ function createServer() {
125
125
  return { content: [{ type: "text", text: JSON.stringify(info, null, 2) }] };
126
126
  });
127
127
 
128
+ // ── AppleScript 실행 (macOS 전용, 앱 직접 제어) ──
129
+ srv.tool("applescript", "AppleScript 실행 (macOS). 메모 작성, 앱 내부 조작 등에 사용", {
130
+ script: z.string().describe("실행할 AppleScript 코드"),
131
+ }, async ({ script }) => {
132
+ const { execSync } = await import("child_process");
133
+ try {
134
+ const output = execSync(`osascript -e '${script.replace(/'/g, "'\\''")}'`, {
135
+ timeout: 15000, encoding: "utf-8", maxBuffer: 1024 * 1024,
136
+ });
137
+ return { content: [{ type: "text", text: output || "(실행 완료)" }] };
138
+ } catch (e) {
139
+ return { content: [{ type: "text", text: `AppleScript 오류: ${e.message}` }] };
140
+ }
141
+ });
142
+
128
143
  return srv;
129
144
  }
130
145
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sweetspot-remote-agent",
3
- "version": "1.5.0",
3
+ "version": "1.7.0",
4
4
  "description": "Sweetspot 원격 제어 MCP 서버 — 스크린샷, 마우스/키보드, 앱 제어, 셸 실행",
5
5
  "type": "module",
6
6
  "main": "mcp-server.js",