@mazees/mark 5.0.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.
Files changed (227) hide show
  1. package/LICENSE +29 -0
  2. package/README.md +209 -0
  3. package/bin/mark.js +27 -0
  4. package/out/renderer/assets/icon-DHo2CYph.svg +1 -0
  5. package/out/renderer/assets/index-G2fADWO-.js +4768 -0
  6. package/out/renderer/assets/index-f3mVse3c.css +1 -0
  7. package/out/renderer/assets/music-cover-CG4vi3kZ.png +0 -0
  8. package/out/renderer/assets/poppins-devanagari-400-normal-CJDn6rn8.woff2 +0 -0
  9. package/out/renderer/assets/poppins-devanagari-400-normal-CqVvlrh5.woff +0 -0
  10. package/out/renderer/assets/poppins-devanagari-500-normal-BIdkeU1p.woff2 +0 -0
  11. package/out/renderer/assets/poppins-devanagari-500-normal-DMPDjHtT.woff +0 -0
  12. package/out/renderer/assets/poppins-devanagari-600-normal-ClASKHrr.woff +0 -0
  13. package/out/renderer/assets/poppins-devanagari-600-normal-STEjXBNN.woff2 +0 -0
  14. package/out/renderer/assets/poppins-devanagari-700-normal-O-jipLrW.woff2 +0 -0
  15. package/out/renderer/assets/poppins-devanagari-700-normal-fHs-vx92.woff +0 -0
  16. package/out/renderer/assets/poppins-latin-400-normal-BOb3E3N0.woff +0 -0
  17. package/out/renderer/assets/poppins-latin-400-normal-cpxAROuN.woff2 +0 -0
  18. package/out/renderer/assets/poppins-latin-500-normal-C8OXljZJ.woff2 +0 -0
  19. package/out/renderer/assets/poppins-latin-500-normal-DGXqpDMm.woff +0 -0
  20. package/out/renderer/assets/poppins-latin-600-normal-BJdTmd5m.woff +0 -0
  21. package/out/renderer/assets/poppins-latin-600-normal-zEkxB9Mr.woff2 +0 -0
  22. package/out/renderer/assets/poppins-latin-700-normal-BVuQR_eA.woff +0 -0
  23. package/out/renderer/assets/poppins-latin-700-normal-Qrb0O0WB.woff2 +0 -0
  24. package/out/renderer/assets/poppins-latin-ext-400-normal-DaBSavcJ.woff +0 -0
  25. package/out/renderer/assets/poppins-latin-ext-400-normal-by3JarPu.woff2 +0 -0
  26. package/out/renderer/assets/poppins-latin-ext-500-normal-CK-6C4Hw.woff2 +0 -0
  27. package/out/renderer/assets/poppins-latin-ext-500-normal-CgAe2rWW.woff +0 -0
  28. package/out/renderer/assets/poppins-latin-ext-600-normal-CAhIAdZj.woff2 +0 -0
  29. package/out/renderer/assets/poppins-latin-ext-600-normal-Df5ffKXP.woff +0 -0
  30. package/out/renderer/assets/poppins-latin-ext-700-normal-DctTR6Tg.woff +0 -0
  31. package/out/renderer/assets/poppins-latin-ext-700-normal-cby-RkWa.woff2 +0 -0
  32. package/out/renderer/favicon.ico +0 -0
  33. package/out/renderer/icon-256.png +0 -0
  34. package/out/renderer/icon.svg +1 -0
  35. package/out/renderer/index.html +25 -0
  36. package/out/renderer/manifest.json +25 -0
  37. package/out/renderer/robot-simulator.html +1640 -0
  38. package/package.json +106 -0
  39. package/src/cli/index.js +332 -0
  40. package/src/cli/monitor.js +413 -0
  41. package/src/cli/theme.js +347 -0
  42. package/src/launcher/MarkTray.cs +227 -0
  43. package/src/main/browser-agent.js +1132 -0
  44. package/src/main/git-service.js +90 -0
  45. package/src/main/google/google-calendar.js +69 -0
  46. package/src/main/google/google-drive.js +263 -0
  47. package/src/main/google/google-gmail.js +131 -0
  48. package/src/main/google/google-service.js +241 -0
  49. package/src/main/node-tools.js +23 -0
  50. package/src/main/pc-agent-scripts/mouse-locker.ps1 +76 -0
  51. package/src/main/pc-agent-scripts/ocr-region.ps1 +185 -0
  52. package/src/main/pc-agent-scripts/pc-daemon.ps1 +726 -0
  53. package/src/main/pc-agent-scripts/pc-overlay.ps1 +253 -0
  54. package/src/main/pc-agent-scripts/pick-folder.ps1 +33 -0
  55. package/src/main/pc-agent-scripts/read-ui.ps1 +181 -0
  56. package/src/main/pc-agent-scripts/take-screenshot.ps1 +24 -0
  57. package/src/main/pc-agent-scripts/win-action.ps1 +380 -0
  58. package/src/main/plugins/plugin-loader.js +306 -0
  59. package/src/main/skills/skill-manager.js +265 -0
  60. package/src/main/syntax-validator.js +455 -0
  61. package/src/main/task-daemon.js +117 -0
  62. package/src/main/telegram/telegram-service.js +835 -0
  63. package/src/main/tools/browser-tools.js +516 -0
  64. package/src/main/tools/file-tools.js +1126 -0
  65. package/src/main/tools/git-tools.js +79 -0
  66. package/src/main/tools/google-tools.js +566 -0
  67. package/src/main/tools/index.js +36 -0
  68. package/src/main/tools/media-tools.js +42 -0
  69. package/src/main/tools/system-tools.js +295 -0
  70. package/src/main/tools/task-tools.js +73 -0
  71. package/src/main/tools/telegram-tools.js +101 -0
  72. package/src/renderer/index.html +24 -0
  73. package/src/renderer/public/favicon.ico +0 -0
  74. package/src/renderer/public/icon-256.png +0 -0
  75. package/src/renderer/public/icon.svg +1 -0
  76. package/src/renderer/public/manifest.json +25 -0
  77. package/src/renderer/public/robot-simulator.html +1640 -0
  78. package/src/renderer/src/App.jsx +304 -0
  79. package/src/renderer/src/api/ai/awareness.js +294 -0
  80. package/src/renderer/src/api/ai/chatSummarizer.js +79 -0
  81. package/src/renderer/src/api/ai/contextCompactor.js +129 -0
  82. package/src/renderer/src/api/ai/contextManager.js +1130 -0
  83. package/src/renderer/src/api/ai/core.js +167 -0
  84. package/src/renderer/src/api/ai/index.js +4 -0
  85. package/src/renderer/src/api/ai/memoryGroomer.js +101 -0
  86. package/src/renderer/src/api/ai/persona.js +115 -0
  87. package/src/renderer/src/api/ai/planning.js +317 -0
  88. package/src/renderer/src/api/ai/relationship.js +177 -0
  89. package/src/renderer/src/api/ai/skillSynthesizer.js +168 -0
  90. package/src/renderer/src/api/ai/tools.js +167 -0
  91. package/src/renderer/src/api/ai/utils.js +359 -0
  92. package/src/renderer/src/api/db.js +1013 -0
  93. package/src/renderer/src/api/oramaStore.js +592 -0
  94. package/src/renderer/src/api/ragPipeline.js +87 -0
  95. package/src/renderer/src/api/subagent/subagentExecutor.js +780 -0
  96. package/src/renderer/src/api/subagent/subagentPrompt.js +44 -0
  97. package/src/renderer/src/api/subagent/subagentStore.js +173 -0
  98. package/src/renderer/src/api/taskExecutor.js +18 -0
  99. package/src/renderer/src/api/taskStore.js +315 -0
  100. package/src/renderer/src/api/tools/core-tools.js +707 -0
  101. package/src/renderer/src/api/tools/index.js +151 -0
  102. package/src/renderer/src/api/turnPairMigrator.js +200 -0
  103. package/src/renderer/src/api/vectorMemory.js +136 -0
  104. package/src/renderer/src/api/wakeWord.js +101 -0
  105. package/src/renderer/src/api/web-bridge.js +885 -0
  106. package/src/renderer/src/api/webSpeech.js +178 -0
  107. package/src/renderer/src/api/workspaceRag.js +76 -0
  108. package/src/renderer/src/assets/electron.svg +10 -0
  109. package/src/renderer/src/assets/icon.svg +1 -0
  110. package/src/renderer/src/assets/main.css +589 -0
  111. package/src/renderer/src/assets/music-cover.png +0 -0
  112. package/src/renderer/src/assets/wavy-lines.svg +25 -0
  113. package/src/renderer/src/components/Chat/ApprovalBubble.jsx +127 -0
  114. package/src/renderer/src/components/Chat/CodeBlock.jsx +83 -0
  115. package/src/renderer/src/components/Chat/DurableTaskBubble.jsx +455 -0
  116. package/src/renderer/src/components/Chat/FileDiffModal.jsx +328 -0
  117. package/src/renderer/src/components/Chat/GeminiWidgets.jsx +82 -0
  118. package/src/renderer/src/components/Chat/MemoryFooterBubble.jsx +34 -0
  119. package/src/renderer/src/components/Chat/MessageBubble.jsx +459 -0
  120. package/src/renderer/src/components/Chat/PluginExecutionBubble.jsx +41 -0
  121. package/src/renderer/src/components/Chat/ThinkingBubble.jsx +316 -0
  122. package/src/renderer/src/components/Chat/YoutubeSearchBubble.jsx +43 -0
  123. package/src/renderer/src/components/Chat/YoutubeSummaryBubble.jsx +40 -0
  124. package/src/renderer/src/components/Chat/index.js +11 -0
  125. package/src/renderer/src/components/ChatList.jsx +434 -0
  126. package/src/renderer/src/components/GlobalCameraManager.jsx +67 -0
  127. package/src/renderer/src/components/Versions.jsx +15 -0
  128. package/src/renderer/src/components/YoutubeMusicPlayer.jsx +375 -0
  129. package/src/renderer/src/components/camera/CameraPreview.jsx +236 -0
  130. package/src/renderer/src/components/config/AiEngineSection.jsx +363 -0
  131. package/src/renderer/src/components/config/CameraSection.jsx +100 -0
  132. package/src/renderer/src/components/config/PermissionsSection.jsx +130 -0
  133. package/src/renderer/src/components/config/ShortcutSection.jsx +102 -0
  134. package/src/renderer/src/components/config/VoiceSection.jsx +110 -0
  135. package/src/renderer/src/components/config/index.js +5 -0
  136. package/src/renderer/src/components/core/Avatar.jsx +2197 -0
  137. package/src/renderer/src/components/core/ConfirmModal.jsx +80 -0
  138. package/src/renderer/src/components/core/DraggableHoloCard.jsx +125 -0
  139. package/src/renderer/src/components/core/FloatingMenu.jsx +272 -0
  140. package/src/renderer/src/components/core/HoloCard.jsx +64 -0
  141. package/src/renderer/src/components/core/InputBar.jsx +1346 -0
  142. package/src/renderer/src/components/core/ProcessPanel.jsx +216 -0
  143. package/src/renderer/src/components/core/ResponseArea.jsx +149 -0
  144. package/src/renderer/src/components/core/StatusIndicator.jsx +59 -0
  145. package/src/renderer/src/components/core/ThoughtNeuralFlow.jsx +168 -0
  146. package/src/renderer/src/components/core/ToolClustersDeck.jsx +210 -0
  147. package/src/renderer/src/components/core/native-skills.js +43 -0
  148. package/src/renderer/src/components/neural-core/EmotionalCortexLobe.jsx +359 -0
  149. package/src/renderer/src/components/neural-core/NodeDissectionModal.jsx +57 -0
  150. package/src/renderer/src/components/neural-core/ProceduralMatrixLobe.jsx +193 -0
  151. package/src/renderer/src/components/neural-core/ProceduralSkillModal.jsx +53 -0
  152. package/src/renderer/src/components/neural-core/ResetAiModal.jsx +198 -0
  153. package/src/renderer/src/components/neural-core/SynapticMemoryLobe.jsx +204 -0
  154. package/src/renderer/src/components/neural-core/traitMeta.js +102 -0
  155. package/src/renderer/src/components/subagent/AgentChatFeed.jsx +603 -0
  156. package/src/renderer/src/components/subagent/AgentInspector.jsx +224 -0
  157. package/src/renderer/src/components/subagent/AgentSidebar.jsx +216 -0
  158. package/src/renderer/src/components/subagent/SubagentIntercom.jsx +572 -0
  159. package/src/renderer/src/components/subagent/SubagentTopologyMap.jsx +423 -0
  160. package/src/renderer/src/contexts/ApprovalContext.jsx +425 -0
  161. package/src/renderer/src/contexts/ChatContext.jsx +61 -0
  162. package/src/renderer/src/contexts/YoutubeMusicContext.jsx +333 -0
  163. package/src/renderer/src/data/faqData.js +202 -0
  164. package/src/renderer/src/hooks/agent/executeAgentTool.js +895 -0
  165. package/src/renderer/src/hooks/agent/index.js +4 -0
  166. package/src/renderer/src/hooks/agent/useMarkMusic.js +75 -0
  167. package/src/renderer/src/hooks/agent/useMarkPlan.js +2739 -0
  168. package/src/renderer/src/hooks/agent/useMarkState.js +173 -0
  169. package/src/renderer/src/hooks/agent/useMarkYoutube.js +82 -0
  170. package/src/renderer/src/hooks/agent/useRelationalGrowth.js +201 -0
  171. package/src/renderer/src/hooks/telegram/useTelegramBot.js +63 -0
  172. package/src/renderer/src/hooks/useAwareness.js +289 -0
  173. package/src/renderer/src/hooks/useChatArchiver.js +52 -0
  174. package/src/renderer/src/hooks/useConfirm.jsx +68 -0
  175. package/src/renderer/src/hooks/useMarkAgent.js +454 -0
  176. package/src/renderer/src/hooks/useMemoryGroomer.js +152 -0
  177. package/src/renderer/src/hooks/useVAD.js +511 -0
  178. package/src/renderer/src/hooks/useWebSpeechWakeWord.js +122 -0
  179. package/src/renderer/src/main.jsx +12 -0
  180. package/src/renderer/src/pages/ChatStudio.jsx +922 -0
  181. package/src/renderer/src/pages/Configuration.jsx +360 -0
  182. package/src/renderer/src/pages/GoogleWorkspace.jsx +293 -0
  183. package/src/renderer/src/pages/Guidebook.jsx +917 -0
  184. package/src/renderer/src/pages/Knowledge.jsx +241 -0
  185. package/src/renderer/src/pages/MarkHome.jsx +491 -0
  186. package/src/renderer/src/pages/NeuralCore.jsx +786 -0
  187. package/src/renderer/src/pages/Plugins.jsx +630 -0
  188. package/src/renderer/src/pages/SkillEditor.jsx +380 -0
  189. package/src/renderer/src/pages/Skills.jsx +219 -0
  190. package/src/renderer/src/pages/Subagents.jsx +342 -0
  191. package/src/renderer/src/pages/TelegramBot.jsx +343 -0
  192. package/src/renderer/src/utils/chatContentParser.js +203 -0
  193. package/src/renderer/src/utils/toolClusters.js +201 -0
  194. package/src/server/bin/sha3_wasm_bg.wasm +0 -0
  195. package/src/server/config-manager.js +57 -0
  196. package/src/server/index.js +195 -0
  197. package/src/server/launcher.js +87 -0
  198. package/src/server/memory/db-store.js +758 -0
  199. package/src/server/memory/orama-store.js +177 -0
  200. package/src/server/memory/vector-engine.js +109 -0
  201. package/src/server/routes/ai.routes.js +280 -0
  202. package/src/server/routes/awareness.routes.js +62 -0
  203. package/src/server/routes/chat.routes.js +61 -0
  204. package/src/server/routes/config.routes.js +41 -0
  205. package/src/server/routes/index.js +35 -0
  206. package/src/server/routes/integrations.routes.js +116 -0
  207. package/src/server/routes/memory.routes.js +256 -0
  208. package/src/server/routes/plugins.routes.js +72 -0
  209. package/src/server/routes/skills.routes.js +139 -0
  210. package/src/server/routes/subagents.routes.js +70 -0
  211. package/src/server/routes/tasks.routes.js +175 -0
  212. package/src/server/routes/workspace.routes.js +127 -0
  213. package/src/server/services/ai/ai-utils.js +161 -0
  214. package/src/server/services/ai/openai-provider.js +757 -0
  215. package/src/server/services/ai/web-provider.js +729 -0
  216. package/src/server/services/ai-bridge.js +46 -0
  217. package/src/server/services/deepseek-web.js +956 -0
  218. package/src/server/services/gemini-web.js +170 -0
  219. package/src/server/services/notification-service.js +91 -0
  220. package/src/server/services/updater.js +125 -0
  221. package/src/server/tools/awareness-tracker.js +292 -0
  222. package/src/server/tools/group-tools.js +1164 -0
  223. package/src/server/tools/media-tools.js +244 -0
  224. package/src/server/tools/pc-agent.js +327 -0
  225. package/src/server/tools/screen-service.js +51 -0
  226. package/src/server/ws-hub.js +168 -0
  227. package/src/shared/parsers/mark-tag-parser.js +275 -0
@@ -0,0 +1,726 @@
1
+ # pc-daemon.ps1
2
+ $ErrorActionPreference = 'SilentlyContinue'
3
+ [Console]::OutputEncoding = [System.Text.Encoding]::UTF8
4
+
5
+ Add-Type -AssemblyName UIAutomationClient
6
+ Add-Type -AssemblyName UIAutomationTypes
7
+ Add-Type -AssemblyName System.Windows.Forms
8
+ Add-Type -AssemblyName System.Drawing
9
+ Add-Type -AssemblyName System.Runtime.WindowsRuntime
10
+
11
+ $code = @"
12
+ using System;
13
+ using System.Runtime.InteropServices;
14
+ using System.Text;
15
+
16
+ public struct RECT {
17
+ public int Left;
18
+ public int Top;
19
+ public int Right;
20
+ public int Bottom;
21
+ }
22
+
23
+ [StructLayout(LayoutKind.Sequential)]
24
+ public struct KEYBDINPUT {
25
+ public ushort wVk;
26
+ public ushort wScan;
27
+ public uint dwFlags;
28
+ public uint time;
29
+ public IntPtr dwExtraInfo;
30
+ }
31
+
32
+ [StructLayout(LayoutKind.Sequential)]
33
+ public struct INPUT {
34
+ public int type;
35
+ public KEYBDINPUT ki;
36
+ }
37
+
38
+ public class MarkWin32 {
39
+ [DllImport("user32.dll")]
40
+ public static extern bool SetCursorPos(int X, int Y);
41
+
42
+ [DllImport("user32.dll")]
43
+ public static extern void mouse_event(uint dwFlags, uint dx, uint dy, int dwData, int dwExtraInfo);
44
+
45
+ [DllImport("user32.dll")]
46
+ public static extern IntPtr GetForegroundWindow();
47
+
48
+ [DllImport("user32.dll")]
49
+ public static extern bool SetForegroundWindow(IntPtr hWnd);
50
+
51
+ [DllImport("user32.dll")]
52
+ public static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
53
+
54
+ [DllImport("user32.dll", CharSet = CharSet.Unicode)]
55
+ public static extern int GetWindowText(IntPtr hWnd, StringBuilder text, int count);
56
+
57
+ [DllImport("user32.dll")]
58
+ public static extern bool GetWindowRect(IntPtr hWnd, out RECT rect);
59
+
60
+ [DllImport("user32.dll")]
61
+ public static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint processId);
62
+
63
+ [DllImport("user32.dll")]
64
+ public static extern bool EnumWindows(EnumWindowsProc enumProc, IntPtr lParam);
65
+
66
+ [DllImport("user32.dll")]
67
+ public static extern bool IsWindowVisible(IntPtr hWnd);
68
+
69
+ [DllImport("user32.dll")]
70
+ public static extern bool IsIconic(IntPtr hWnd);
71
+
72
+ [DllImport("user32.dll", CharSet = CharSet.Unicode)]
73
+ public static extern int GetWindowTextLength(IntPtr hWnd);
74
+
75
+ public delegate bool EnumWindowsProc(IntPtr hWnd, IntPtr lParam);
76
+
77
+ public const uint MOUSEEVENTF_LEFTDOWN = 0x0002;
78
+ public const uint MOUSEEVENTF_LEFTUP = 0x0004;
79
+ public const uint MOUSEEVENTF_RIGHTDOWN = 0x0008;
80
+ public const uint MOUSEEVENTF_RIGHTUP = 0x0010;
81
+ public const uint MOUSEEVENTF_WHEEL = 0x0800;
82
+
83
+ [DllImport("user32.dll", SetLastError = true)]
84
+ public static extern uint SendInput(uint nInputs, INPUT[] pInputs, int cbSize);
85
+
86
+ public const int INPUT_KEYBOARD = 1;
87
+ public const uint KEYEVENTF_KEYUP = 0x0002;
88
+ public const uint KEYEVENTF_UNICODE = 0x0004;
89
+ public const ushort VK_RETURN = 0x0D;
90
+
91
+ [DllImport("user32.dll")]
92
+ public static extern void keybd_event(byte bVk, byte bScan, uint dwFlags, int dwExtraInfo);
93
+ public const int KEYEVENTF_EXTENDEDKEY = 0x0001;
94
+ public const byte VK_LWIN = 0x5B;
95
+
96
+ public static string ListWindowsJson() {
97
+ StringBuilder json = new StringBuilder();
98
+ json.Append("[");
99
+ bool first = true;
100
+ EnumWindows(delegate(IntPtr hWnd, IntPtr lParam) {
101
+ int len = GetWindowTextLength(hWnd);
102
+ if (len > 0) {
103
+ StringBuilder sb = new StringBuilder(len + 1);
104
+ GetWindowText(hWnd, sb, sb.Capacity);
105
+ string title = sb.ToString();
106
+ if (!string.IsNullOrWhiteSpace(title) && title != "Program Manager" && title != "Default IME" && !title.StartsWith("MSCTFIME")) {
107
+ if (!first) json.Append(",");
108
+ first = false;
109
+ json.Append("{\"hwnd\":").Append(hWnd.ToInt64()).Append(",\"title\":\"").Append(title.Replace("\\", "\\\\").Replace("\"", "\\\"")).Append("\"}");
110
+ }
111
+ }
112
+ return true;
113
+ }, IntPtr.Zero);
114
+ json.Append("]");
115
+ return json.ToString();
116
+ }
117
+
118
+ public static string FocusWindowByTitle(string target) {
119
+ string lowerTarget = target.ToLower();
120
+ bool found = false;
121
+ string foundTitle = "";
122
+ EnumWindows(delegate(IntPtr hWnd, IntPtr lParam) {
123
+ int len = GetWindowTextLength(hWnd);
124
+ if (len > 0) {
125
+ StringBuilder sb = new StringBuilder(len + 1);
126
+ GetWindowText(hWnd, sb, sb.Capacity);
127
+ string title = sb.ToString();
128
+ if (title.ToLower().Contains(lowerTarget)) {
129
+ ShowWindow(hWnd, 9); // SW_RESTORE = 9
130
+ SetForegroundWindow(hWnd);
131
+ found = true;
132
+ foundTitle = title;
133
+ return false;
134
+ }
135
+ }
136
+ return true;
137
+ }, IntPtr.Zero);
138
+ if (found) {
139
+ return "{\"status\":\"success\",\"action\":\"focus-window\",\"title\":\"" + foundTitle.Replace("\\", "\\\\").Replace("\"", "\\\"") + "\"}";
140
+ }
141
+ return "{\"status\":\"error\",\"message\":\"Window not found: " + target.Replace("\\", "\\\\").Replace("\"", "\\\"") + "\"}";
142
+ }
143
+
144
+ public static bool IsMarkWindow(string title) {
145
+ if (string.IsNullOrWhiteSpace(title)) return true;
146
+ string t = title.Trim();
147
+ if (t == "Program Manager" || t == "Default IME" || t.StartsWith("MSCTFIME")) return true;
148
+ string lower = t.ToLower();
149
+ if (lower == "mark" || lower.StartsWith("mark -") || lower.StartsWith("mark_pc_stop") || lower.StartsWith("mark_pc_abort") || lower.StartsWith("mark_unblock") || lower.Contains("mark agent") || lower.Contains("mark pc automation")) {
150
+ return true;
151
+ }
152
+ return false;
153
+ }
154
+
155
+ public static IntPtr GetTargetWindow() {
156
+ IntPtr fg = GetForegroundWindow();
157
+ string fgTitle = "";
158
+ int fgLen = GetWindowTextLength(fg);
159
+ if (fgLen > 0) {
160
+ StringBuilder fgSb = new StringBuilder(fgLen + 1);
161
+ GetWindowText(fg, fgSb, fgSb.Capacity);
162
+ fgTitle = fgSb.ToString();
163
+ }
164
+
165
+ if (!IsMarkWindow(fgTitle)) {
166
+ return fg;
167
+ }
168
+
169
+ IntPtr target = IntPtr.Zero;
170
+ EnumWindows(delegate(IntPtr hWnd, IntPtr lParam) {
171
+ if (!IsWindowVisible(hWnd) || IsIconic(hWnd)) return true;
172
+ int len = GetWindowTextLength(hWnd);
173
+ if (len <= 0) return true;
174
+ StringBuilder sb = new StringBuilder(len + 1);
175
+ GetWindowText(hWnd, sb, sb.Capacity);
176
+ string title = sb.ToString();
177
+
178
+ if (!IsMarkWindow(title)) {
179
+ target = hWnd;
180
+ return false;
181
+ }
182
+ return true;
183
+ }, IntPtr.Zero);
184
+
185
+ if (target != IntPtr.Zero) {
186
+ return target;
187
+ }
188
+ return fg;
189
+ }
190
+
191
+ public static void EnsureTargetWindowFocused() {
192
+ IntPtr target = GetTargetWindow();
193
+ if (target != IntPtr.Zero) {
194
+ SetForegroundWindow(target);
195
+ System.Threading.Thread.Sleep(200);
196
+ }
197
+ }
198
+
199
+ public static string TypeTextUnicode(string text) {
200
+ string normalized = text.Replace("\r\n", "\n").Replace("\r", "\n");
201
+ foreach (char c in normalized) {
202
+ if (c == '\n') {
203
+ INPUT[] inputs = new INPUT[2];
204
+ inputs[0].type = INPUT_KEYBOARD;
205
+ inputs[0].ki.wVk = VK_RETURN;
206
+ inputs[0].ki.wScan = 0;
207
+ inputs[0].ki.dwFlags = 0;
208
+
209
+ inputs[1].type = INPUT_KEYBOARD;
210
+ inputs[1].ki.wVk = VK_RETURN;
211
+ inputs[1].ki.wScan = 0;
212
+ inputs[1].ki.dwFlags = KEYEVENTF_KEYUP;
213
+
214
+ SendInput(2, inputs, Marshal.SizeOf(typeof(INPUT)));
215
+ System.Threading.Thread.Sleep(15);
216
+ } else {
217
+ INPUT[] inputs = new INPUT[2];
218
+ inputs[0].type = INPUT_KEYBOARD;
219
+ inputs[0].ki.wVk = 0;
220
+ inputs[0].ki.wScan = (ushort)c;
221
+ inputs[0].ki.dwFlags = KEYEVENTF_UNICODE;
222
+
223
+ inputs[1].type = INPUT_KEYBOARD;
224
+ inputs[1].ki.wVk = 0;
225
+ inputs[1].ki.wScan = (ushort)c;
226
+ inputs[1].ki.dwFlags = KEYEVENTF_UNICODE | KEYEVENTF_KEYUP;
227
+
228
+ SendInput(2, inputs, Marshal.SizeOf(typeof(INPUT)));
229
+ System.Threading.Thread.Sleep(8);
230
+ }
231
+ }
232
+ return "{\"status\":\"success\",\"action\":\"type\",\"text\":\"" + text.Replace("\\", "\\\\").Replace("\"", "\\\"").Replace("\r", "\\r").Replace("\n", "\\n") + "\"}";
233
+ }
234
+
235
+ [StructLayout(LayoutKind.Sequential)]
236
+ public struct LASTINPUTINFO {
237
+ public uint cbSize;
238
+ public uint dwTime;
239
+ }
240
+
241
+ [DllImport("user32.dll")]
242
+ public static extern bool GetLastInputInfo(ref LASTINPUTINFO plii);
243
+
244
+ public static int GetIdleSeconds() {
245
+ LASTINPUTINFO lii = new LASTINPUTINFO();
246
+ lii.cbSize = (uint)Marshal.SizeOf(lii);
247
+ if (GetLastInputInfo(ref lii)) {
248
+ uint idleMs = (uint)Environment.TickCount - lii.dwTime;
249
+ return (int)(idleMs / 1000);
250
+ }
251
+ return 0;
252
+ }
253
+ }
254
+ "@
255
+ Add-Type -TypeDefinition $code -Language CSharp
256
+
257
+ function Escape-SendKeys {
258
+ param([string]$str)
259
+ if ([string]::IsNullOrEmpty($str)) { return "" }
260
+ $res = ""
261
+ foreach ($char in $str.ToCharArray()) {
262
+ if ("+^%~(){}[]".Contains($char.ToString())) {
263
+ $res += "{$char}"
264
+ } elseif ($char -eq "`n" -or $char -eq "`r") {
265
+ $res += "{ENTER}"
266
+ } else {
267
+ $res += $char
268
+ }
269
+ }
270
+ return $res
271
+ }
272
+
273
+ Write-Output '{"status":"ready"}'
274
+ Write-Output "---MARK_DONE---"
275
+ [Console]::Out.Flush()
276
+
277
+ $global:ElementCache = @{}
278
+
279
+ while ($true) {
280
+ $line = [Console]::ReadLine()
281
+ if ($null -eq $line) { break }
282
+ $line = $line.Trim()
283
+ if ($line -eq "") { continue }
284
+
285
+ try {
286
+ $cmdObj = $line | ConvertFrom-Json
287
+ $cmd = $cmdObj.cmd
288
+
289
+ if ($cmd -eq "exit") { break }
290
+
291
+ switch ($cmd) {
292
+ "get-idle" {
293
+ $idle = [MarkWin32]::GetIdleSeconds()
294
+ Write-Output (@{ status="success"; idleSeconds=$idle } | ConvertTo-Json -Compress)
295
+ }
296
+ "get-active-window" {
297
+ $hwnd = [MarkWin32]::GetForegroundWindow()
298
+ $titleBuilder = New-Object System.Text.StringBuilder 512
299
+ [MarkWin32]::GetWindowText($hwnd, $titleBuilder, $titleBuilder.Capacity) | Out-Null
300
+ $windowTitle = $titleBuilder.ToString()
301
+
302
+ $processId = 0
303
+ [MarkWin32]::GetWindowThreadProcessId($hwnd, [ref]$processId) | Out-Null
304
+ $procName = "Windows App"
305
+ if ($processId -gt 0) {
306
+ try {
307
+ $p = Get-Process -Id $processId -ErrorAction SilentlyContinue
308
+ if ($p) { $procName = $p.ProcessName }
309
+ } catch {}
310
+ }
311
+ Write-Output (@{ status="success"; title=$windowTitle; process=$procName } | ConvertTo-Json -Compress)
312
+ }
313
+ "read-focus" {
314
+ $global:ElementCache.Clear()
315
+ $hwnd = [MarkWin32]::GetForegroundWindow()
316
+ $titleBuilder = New-Object System.Text.StringBuilder 512
317
+ [MarkWin32]::GetWindowText($hwnd, $titleBuilder, $titleBuilder.Capacity) | Out-Null
318
+ $windowTitle = $titleBuilder.ToString()
319
+
320
+ $processId = 0
321
+ [MarkWin32]::GetWindowThreadProcessId($hwnd, [ref]$processId) | Out-Null
322
+ $procName = "Windows App"
323
+ if ($processId -gt 0) {
324
+ try {
325
+ $p = Get-Process -Id $processId -ErrorAction SilentlyContinue
326
+ if ($p) { $procName = $p.ProcessName }
327
+ } catch {}
328
+ }
329
+
330
+ $elements = @()
331
+ try {
332
+ $el = [System.Windows.Automation.AutomationElement]::FocusedElement
333
+ if ($el) {
334
+ $role = $el.Current.ControlType.ProgrammaticName.Replace("ControlType.", "")
335
+ $name = $el.Current.Name
336
+ $autoId = $el.Current.AutomationId
337
+ $rect = $el.Current.BoundingRectangle
338
+
339
+ $val = ""
340
+ try {
341
+ $valuePattern = $el.GetCurrentPattern([System.Windows.Automation.ValuePattern]::Pattern)
342
+ if ($valuePattern) { $val = $valuePattern.Current.Value }
343
+ } catch {}
344
+
345
+ $elementObj = @{
346
+ id = 1
347
+ name = if ($name) { $name } else { $autoId }
348
+ role = $role
349
+ rect = @([int]$rect.X, [int]$rect.Y, [int]$rect.Width, [int]$rect.Height)
350
+ }
351
+ if ($val) { $elementObj["value"] = $val }
352
+
353
+ $global:ElementCache[1] = $el
354
+ $elements += $elementObj
355
+ }
356
+ } catch {}
357
+
358
+ $output = @{
359
+ window = $windowTitle
360
+ process = $procName
361
+ elements = $elements
362
+ element_count = $elements.Count
363
+ method = "uiautomation-focus"
364
+ }
365
+ Write-Output ($output | ConvertTo-Json -Depth 5 -Compress)
366
+ }
367
+ "read-ui" {
368
+ $global:ElementCache.Clear()
369
+ $maxElements = if ($cmdObj.maxElements) { [int]$cmdObj.maxElements } else { 300 }
370
+ $filterRoles = $cmdObj.roles
371
+
372
+ $hwnd = [MarkWin32]::GetTargetWindow()
373
+ $titleBuilder = New-Object System.Text.StringBuilder 512
374
+ [MarkWin32]::GetWindowText($hwnd, $titleBuilder, $titleBuilder.Capacity) | Out-Null
375
+ $windowTitle = $titleBuilder.ToString()
376
+
377
+ $processId = 0
378
+ [MarkWin32]::GetWindowThreadProcessId($hwnd, [ref]$processId) | Out-Null
379
+ $processName = "unknown"
380
+ if ($processId -gt 0) {
381
+ try {
382
+ $proc = Get-Process -Id $processId
383
+ $processName = $proc.ProcessName + ".exe"
384
+ } catch {}
385
+ }
386
+
387
+ $elements = @()
388
+ $idCounter = 1
389
+
390
+ try {
391
+ $windowElement = [System.Windows.Automation.AutomationElement]::FromHandle($hwnd)
392
+ if ($windowElement) {
393
+ $condition = [System.Windows.Automation.Condition]::TrueCondition
394
+
395
+ $allControls = $windowElement.FindAll([System.Windows.Automation.TreeScope]::Descendants, $condition)
396
+
397
+ foreach ($el in $allControls) {
398
+ if ($idCounter -gt $maxElements) { break }
399
+
400
+ $enabled = $el.Current.IsEnabled
401
+ $offscreen = $el.Current.IsOffscreen
402
+ if (-not $enabled -or $offscreen) { continue }
403
+
404
+ $role = $el.Current.ControlType.ProgrammaticName.Replace("ControlType.", "")
405
+
406
+ if ($filterRoles) {
407
+ if ($filterRoles -notcontains $role) { continue }
408
+ } else {
409
+ $interactiveRoles = @("Button", "Edit", "MenuItem", "TabItem", "ComboBox", "CheckBox", "RadioButton", "Hyperlink", "ListItem", "TreeItem", "DataItem", "Text")
410
+ if ($interactiveRoles -notcontains $role) { continue }
411
+ }
412
+
413
+ $name = $el.Current.Name
414
+ $autoId = $el.Current.AutomationId
415
+ $rect = $el.Current.BoundingRectangle
416
+
417
+ if ([string]::IsNullOrWhiteSpace($name) -and [string]::IsNullOrWhiteSpace($autoId) -and $role -ne "Edit") {
418
+ continue
419
+ }
420
+
421
+ $val = ""
422
+ if ($role -eq "Edit") {
423
+ try {
424
+ $valuePattern = $el.GetCurrentPattern([System.Windows.Automation.ValuePattern]::Pattern)
425
+ if ($valuePattern) { $val = $valuePattern.Current.Value }
426
+ } catch {}
427
+ }
428
+
429
+ $elementObj = @{
430
+ id = $idCounter
431
+ name = if ($name) { $name } else { $autoId }
432
+ role = $role
433
+ rect = @([int]$rect.X, [int]$rect.Y, [int]$rect.Width, [int]$rect.Height)
434
+ }
435
+ if ($val) {
436
+ $elementObj["value"] = $val
437
+ }
438
+
439
+ $global:ElementCache[$idCounter] = $el
440
+ $elements += $elementObj
441
+ $idCounter++
442
+ }
443
+ }
444
+ } catch {
445
+ }
446
+
447
+ $output = @{
448
+ window = $windowTitle
449
+ process = $processName
450
+ elements = $elements
451
+ element_count = $elements.Count
452
+ method = "uiautomation"
453
+ }
454
+ Write-Output ($output | ConvertTo-Json -Depth 5 -Compress)
455
+ }
456
+ "ocr" {
457
+ $hwnd = [MarkWin32]::GetTargetWindow()
458
+ $titleBuilder = New-Object System.Text.StringBuilder 512
459
+ [MarkWin32]::GetWindowText($hwnd, $titleBuilder, $titleBuilder.Capacity) | Out-Null
460
+ $windowTitle = $titleBuilder.ToString()
461
+
462
+ $rect = New-Object RECT
463
+ [MarkWin32]::GetWindowRect($hwnd, [ref]$rect) | Out-Null
464
+
465
+ $width = $rect.Right - $rect.Left
466
+ $height = $rect.Bottom - $rect.Top
467
+
468
+ if ($width -le 0 -or $height -le 0) {
469
+ $width = [System.Windows.Forms.Screen]::PrimaryScreen.Bounds.Width
470
+ $height = [System.Windows.Forms.Screen]::PrimaryScreen.Bounds.Height
471
+ $rect.Left = 0
472
+ $rect.Top = 0
473
+ }
474
+
475
+ $bmp = New-Object System.Drawing.Bitmap($width, $height)
476
+ $graphics = [System.Drawing.Graphics]::FromImage($bmp)
477
+ $graphics.CopyFromScreen($rect.Left, $rect.Top, 0, 0, $bmp.Size)
478
+ $tempPath = [System.IO.Path]::Combine($env:TEMP, "mark_ocr_temp_$([Guid]::NewGuid().ToString('N')).png")
479
+ $bmp.Save($tempPath, [System.Drawing.Imaging.ImageFormat]::Png)
480
+ $graphics.Dispose()
481
+ $bmp.Dispose()
482
+
483
+ $detectedText = @()
484
+ try {
485
+ $ocrEngine = [Windows.Media.Ocr.OcrEngine, Windows.Foundation, ContentType = WindowsRuntime]::TryCreateFromUserProfileLanguages()
486
+ if ($ocrEngine) {
487
+ $file = [Windows.Storage.StorageFile, Windows.Storage, ContentType = WindowsRuntime]::GetFileFromPathAsync($tempPath).GetAwaiter().GetResult()
488
+ $stream = $file.OpenAsync([Windows.Storage.FileAccessMode, Windows.Storage, ContentType = WindowsRuntime]::Read).GetAwaiter().GetResult()
489
+ $decoder = [Windows.Graphics.Imaging.BitmapDecoder, Windows.Graphics, ContentType = WindowsRuntime]::CreateAsync($stream).GetAwaiter().GetResult()
490
+ $bitmap = $decoder.GetSoftwareBitmapAsync().GetAwaiter().GetResult()
491
+
492
+ $result = $ocrEngine.RecognizeAsync($bitmap).GetAwaiter().GetResult()
493
+
494
+ $idCounter = 1
495
+ foreach ($line in $result.Lines) {
496
+ $lineText = $line.Text
497
+ if ([string]::IsNullOrWhiteSpace($lineText)) { continue }
498
+
499
+ $firstWord = $line.Words[0]
500
+ $lastWord = $line.Words[$line.Words.Count - 1]
501
+
502
+ $lineX = $rect.Left + [int]$firstWord.BoundingRect.X
503
+ $lineY = $rect.Top + [int]$firstWord.BoundingRect.Y
504
+ $lineW = [int]($lastWord.BoundingRect.X + $lastWord.BoundingRect.Width - $firstWord.BoundingRect.X)
505
+ $lineH = [int]$firstWord.BoundingRect.Height
506
+
507
+ $detectedText += @{
508
+ id = $idCounter
509
+ text = $lineText
510
+ rect = @($lineX, $lineY, $lineW, $lineH)
511
+ }
512
+ $idCounter++
513
+ if ($idCounter -gt 60) { break }
514
+ }
515
+ }
516
+ } catch {
517
+ } finally {
518
+ if (Test-Path $tempPath) {
519
+ Remove-Item $tempPath -Force -ErrorAction SilentlyContinue
520
+ }
521
+ }
522
+
523
+ $output = @{
524
+ window = $windowTitle
525
+ method = "ocr"
526
+ elements = $detectedText
527
+ element_count = $detectedText.Count
528
+ }
529
+ Write-Output ($output | ConvertTo-Json -Depth 5 -Compress)
530
+ }
531
+ "native-invoke" {
532
+ $id = [int]$cmdObj.id
533
+ $element = $global:ElementCache[$id]
534
+ if ($element -ne $null) {
535
+ $success = $false
536
+ try {
537
+ $invokePattern = $element.GetCurrentPattern([System.Windows.Automation.InvokePattern]::Pattern)
538
+ $invokePattern.Invoke()
539
+ $success = $true
540
+ Write-Output (@{ status="success"; action="native-invoke"; id=$id } | ConvertTo-Json -Compress)
541
+ } catch {
542
+ try {
543
+ $togglePattern = $element.GetCurrentPattern([System.Windows.Automation.TogglePattern]::Pattern)
544
+ $togglePattern.Toggle()
545
+ $success = $true
546
+ Write-Output (@{ status="success"; action="native-invoke-toggle"; id=$id } | ConvertTo-Json -Compress)
547
+ } catch {}
548
+ }
549
+
550
+ if (-not $success) {
551
+ try {
552
+ $rect = $element.Current.BoundingRectangle
553
+ if ($rect.Width -gt 0 -and $rect.Height -gt 0) {
554
+ $x = [int]($rect.X + ($rect.Width / 2))
555
+ $y = [int]($rect.Y + ($rect.Height / 2))
556
+ [MarkWin32]::EnsureTargetWindowFocused()
557
+ [MarkWin32]::SetCursorPos($x, $y) | Out-Null
558
+ Start-Sleep -Milliseconds 20
559
+ [MarkWin32]::mouse_event([MarkWin32]::MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0)
560
+ Start-Sleep -Milliseconds 20
561
+ [MarkWin32]::mouse_event([MarkWin32]::MOUSEEVENTF_LEFTUP, 0, 0, 0, 0)
562
+ Write-Output (@{ status="success"; action="native-invoke-fallback-click"; id=$id; x=$x; y=$y } | ConvertTo-Json -Compress)
563
+ } else {
564
+ Write-Output (@{ status="error"; message="Elemen tidak memiliki InvokePattern dan tidak terlihat di layar (BoundingRect kosong)" } | ConvertTo-Json -Compress)
565
+ }
566
+ } catch {
567
+ Write-Output (@{ status="error"; message="Elemen tidak memiliki InvokePattern dan gagal fallback klik fisik" } | ConvertTo-Json -Compress)
568
+ }
569
+ }
570
+ } else {
571
+ Write-Output (@{ status="error"; message="ID Elemen tidak ditemukan di cache (Mungkin kadaluarsa, lakukan os-read ulang)" } | ConvertTo-Json -Compress)
572
+ }
573
+ }
574
+ "click" {
575
+ [MarkWin32]::EnsureTargetWindowFocused()
576
+ $x = [int]$cmdObj.x
577
+ $y = [int]$cmdObj.y
578
+ [MarkWin32]::SetCursorPos($x, $y) | Out-Null
579
+ Start-Sleep -Milliseconds 50
580
+ [MarkWin32]::mouse_event([MarkWin32]::MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0)
581
+ Start-Sleep -Milliseconds 30
582
+ [MarkWin32]::mouse_event([MarkWin32]::MOUSEEVENTF_LEFTUP, 0, 0, 0, 0)
583
+
584
+ Write-Output (@{ status="success"; action="click"; x=$x; y=$y } | ConvertTo-Json -Compress)
585
+ }
586
+ "double-click" {
587
+ [MarkWin32]::EnsureTargetWindowFocused()
588
+ $x = [int]$cmdObj.x
589
+ $y = [int]$cmdObj.y
590
+ [MarkWin32]::SetCursorPos($x, $y) | Out-Null
591
+ Start-Sleep -Milliseconds 50
592
+ [MarkWin32]::mouse_event([MarkWin32]::MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0)
593
+ Start-Sleep -Milliseconds 30
594
+ [MarkWin32]::mouse_event([MarkWin32]::MOUSEEVENTF_LEFTUP, 0, 0, 0, 0)
595
+ Start-Sleep -Milliseconds 50
596
+ [MarkWin32]::mouse_event([MarkWin32]::MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0)
597
+ Start-Sleep -Milliseconds 30
598
+ [MarkWin32]::mouse_event([MarkWin32]::MOUSEEVENTF_LEFTUP, 0, 0, 0, 0)
599
+
600
+ Write-Output (@{ status="success"; action="double-click"; x=$x; y=$y } | ConvertTo-Json -Compress)
601
+ }
602
+ "type" {
603
+ [MarkWin32]::EnsureTargetWindowFocused()
604
+ $text = $cmdObj.text
605
+ if (-not [string]::IsNullOrEmpty($text)) {
606
+ $escaped = Escape-SendKeys -str $text
607
+ try {
608
+ [System.Windows.Forms.SendKeys]::SendWait($escaped)
609
+ Write-Output (@{ status="success"; action="type"; text=$text } | ConvertTo-Json -Compress)
610
+ } catch {
611
+ Write-Output (@{ status="error"; message="SendKeys failed for type" } | ConvertTo-Json -Compress)
612
+ }
613
+ } else {
614
+ Write-Output (@{ status="error"; message="Empty text" } | ConvertTo-Json -Compress)
615
+ }
616
+ }
617
+ "key" {
618
+ [MarkWin32]::EnsureTargetWindowFocused()
619
+ $combo = $cmdObj.combo
620
+ if (-not [string]::IsNullOrEmpty($combo)) {
621
+ $keys = $combo.ToLower().Trim()
622
+ $modifiers = ""
623
+
624
+ if ($keys -match "ctrl\+") { $modifiers += "^"; $keys = $keys -replace "ctrl\+", "" }
625
+ if ($keys -match "alt\+") { $modifiers += "%"; $keys = $keys -replace "alt\+", "" }
626
+ if ($keys -match "shift\+") { $modifiers += "+"; $keys = $keys -replace "shift\+", "" }
627
+ if ($keys -match "win\+") { $keys = $keys -replace "win\+", "" } # SendKeys doesn't support Win key combo directly, but we strip it
628
+
629
+ $specialMap = @{
630
+ "enter" = "{ENTER}"
631
+ "tab" = "{TAB}"
632
+ "esc" = "{ESC}"
633
+ "escape" = "{ESC}"
634
+ "backspace" = "{BACKSPACE}"
635
+ "del" = "{DELETE}"
636
+ "delete" = "{DELETE}"
637
+ "up" = "{UP}"
638
+ "down" = "{DOWN}"
639
+ "left" = "{LEFT}"
640
+ "right" = "{RIGHT}"
641
+ "home" = "{HOME}"
642
+ "end" = "{END}"
643
+ "space" = " "
644
+ }
645
+
646
+ if ($keys -eq "win") {
647
+ [MarkWin32]::keybd_event([MarkWin32]::VK_LWIN, 0, 0, 0)
648
+ Start-Sleep -Milliseconds 20
649
+ [MarkWin32]::keybd_event([MarkWin32]::VK_LWIN, 0, [MarkWin32]::KEYEVENTF_KEYUP, 0)
650
+ Write-Output (@{ status="success"; action="key"; combo=$combo } | ConvertTo-Json -Compress)
651
+ continue
652
+ }
653
+
654
+ if ($specialMap.ContainsKey($keys)) {
655
+ $sendStr = $modifiers + $specialMap[$keys]
656
+ } else {
657
+ if ($keys.Length -eq 1) {
658
+ $sendStr = $modifiers + $keys
659
+ } else {
660
+ $sendStr = $modifiers + "{" + $keys + "}"
661
+ }
662
+ }
663
+
664
+ try {
665
+ [System.Windows.Forms.SendKeys]::SendWait($sendStr)
666
+ Write-Output (@{ status="success"; action="key"; combo=$combo } | ConvertTo-Json -Compress)
667
+ } catch {
668
+ Write-Output (@{ status="error"; message="Invalid key combo: $combo" } | ConvertTo-Json -Compress)
669
+ }
670
+ } else {
671
+ Write-Output (@{ status="error"; message="Empty combo" } | ConvertTo-Json -Compress)
672
+ }
673
+ }
674
+ "scroll" {
675
+ [MarkWin32]::EnsureTargetWindowFocused()
676
+ $direction = if ($cmdObj.direction) { $cmdObj.direction } else { "down" }
677
+ $amount = if ($cmdObj.amount) { [int]$cmdObj.amount } else { 3 }
678
+
679
+ $delta = 120 * $amount
680
+ if ($direction -eq "down") {
681
+ $delta = -$delta
682
+ }
683
+ [MarkWin32]::mouse_event([MarkWin32]::MOUSEEVENTF_WHEEL, 0, 0, $delta, 0)
684
+ Write-Output (@{ status="success"; action="scroll"; direction=$direction; amount=$amount } | ConvertTo-Json -Compress)
685
+ }
686
+ "open" {
687
+ $target = $cmdObj.target
688
+ if (-not [string]::IsNullOrEmpty($target)) {
689
+ try {
690
+ Start-Process -FilePath $target
691
+ Write-Output (@{ status="success"; action="open"; target=$target } | ConvertTo-Json -Compress)
692
+ } catch {
693
+ Write-Output (@{ status="error"; message="Failed to open app: $target" } | ConvertTo-Json -Compress)
694
+ }
695
+ } else {
696
+ Write-Output (@{ status="error"; message="Empty target" } | ConvertTo-Json -Compress)
697
+ }
698
+ }
699
+ "list-windows" {
700
+ $res = [MarkWin32]::ListWindowsJson()
701
+ Write-Output $res
702
+ }
703
+ "focus-window" {
704
+ $title = $cmdObj.title
705
+ if (-not [string]::IsNullOrEmpty($title)) {
706
+ $res = [MarkWin32]::FocusWindowByTitle($title)
707
+ Write-Output $res
708
+ } else {
709
+ Write-Output (@{ status="error"; message="Empty title" } | ConvertTo-Json -Compress)
710
+ }
711
+ }
712
+ "ping" {
713
+ Write-Output (@{ status="alive" } | ConvertTo-Json -Compress)
714
+ }
715
+ default {
716
+ Write-Output (@{ status="error"; message="Unknown command: $cmd" } | ConvertTo-Json -Compress)
717
+ }
718
+ }
719
+ } catch {
720
+ $err = @{ status = "error"; message = $_.Exception.Message }
721
+ Write-Output ($err | ConvertTo-Json -Compress)
722
+ }
723
+
724
+ Write-Output "---MARK_DONE---"
725
+ [Console]::Out.Flush()
726
+ }