@mseep/clawdcursor 1.5.5

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 (354) hide show
  1. package/CHANGELOG.md +2264 -0
  2. package/LICENSE +21 -0
  3. package/README.md +385 -0
  4. package/SECURITY.md +44 -0
  5. package/SKILL.md +503 -0
  6. package/dist/core/agent-loop/agent.d.ts +42 -0
  7. package/dist/core/agent-loop/agent.js +1023 -0
  8. package/dist/core/agent-loop/agent.js.map +1 -0
  9. package/dist/core/agent-loop/batch-tool.d.ts +25 -0
  10. package/dist/core/agent-loop/batch-tool.js +218 -0
  11. package/dist/core/agent-loop/batch-tool.js.map +1 -0
  12. package/dist/core/agent-loop/coord-scale.d.ts +72 -0
  13. package/dist/core/agent-loop/coord-scale.js +89 -0
  14. package/dist/core/agent-loop/coord-scale.js.map +1 -0
  15. package/dist/core/agent-loop/focus-guard.d.ts +24 -0
  16. package/dist/core/agent-loop/focus-guard.js +29 -0
  17. package/dist/core/agent-loop/focus-guard.js.map +1 -0
  18. package/dist/core/agent-loop/project-mcp.d.ts +97 -0
  19. package/dist/core/agent-loop/project-mcp.js +253 -0
  20. package/dist/core/agent-loop/project-mcp.js.map +1 -0
  21. package/dist/core/agent-loop/prompt.d.ts +45 -0
  22. package/dist/core/agent-loop/prompt.js +426 -0
  23. package/dist/core/agent-loop/prompt.js.map +1 -0
  24. package/dist/core/agent-loop/tool-meta.d.ts +93 -0
  25. package/dist/core/agent-loop/tool-meta.js +651 -0
  26. package/dist/core/agent-loop/tool-meta.js.map +1 -0
  27. package/dist/core/agent-loop/tools.d.ts +38 -0
  28. package/dist/core/agent-loop/tools.js +2134 -0
  29. package/dist/core/agent-loop/tools.js.map +1 -0
  30. package/dist/core/agent-loop/types.d.ts +170 -0
  31. package/dist/core/agent-loop/types.js +12 -0
  32. package/dist/core/agent-loop/types.js.map +1 -0
  33. package/dist/core/agent.d.ts +51 -0
  34. package/dist/core/agent.js +245 -0
  35. package/dist/core/agent.js.map +1 -0
  36. package/dist/core/app-categories.d.ts +67 -0
  37. package/dist/core/app-categories.js +108 -0
  38. package/dist/core/app-categories.js.map +1 -0
  39. package/dist/core/banner.d.ts +70 -0
  40. package/dist/core/banner.js +245 -0
  41. package/dist/core/banner.js.map +1 -0
  42. package/dist/core/classify/capability.d.ts +45 -0
  43. package/dist/core/classify/capability.js +78 -0
  44. package/dist/core/classify/capability.js.map +1 -0
  45. package/dist/core/decompose/llm-decomposer.d.ts +35 -0
  46. package/dist/core/decompose/llm-decomposer.js +156 -0
  47. package/dist/core/decompose/llm-decomposer.js.map +1 -0
  48. package/dist/core/decompose/parser.d.ts +27 -0
  49. package/dist/core/decompose/parser.js +101 -0
  50. package/dist/core/decompose/parser.js.map +1 -0
  51. package/dist/core/observability/correlation.d.ts +19 -0
  52. package/dist/core/observability/correlation.js +36 -0
  53. package/dist/core/observability/correlation.js.map +1 -0
  54. package/dist/core/observability/cost-meter.d.ts +51 -0
  55. package/dist/core/observability/cost-meter.js +134 -0
  56. package/dist/core/observability/cost-meter.js.map +1 -0
  57. package/dist/core/observability/logger.d.ts +61 -0
  58. package/dist/core/observability/logger.js +550 -0
  59. package/dist/core/observability/logger.js.map +1 -0
  60. package/dist/core/router/aliases.d.ts +50 -0
  61. package/dist/core/router/aliases.js +104 -0
  62. package/dist/core/router/aliases.js.map +1 -0
  63. package/dist/core/router/normalize.d.ts +41 -0
  64. package/dist/core/router/normalize.js +80 -0
  65. package/dist/core/router/normalize.js.map +1 -0
  66. package/dist/core/safety.d.ts +126 -0
  67. package/dist/core/safety.js +568 -0
  68. package/dist/core/safety.js.map +1 -0
  69. package/dist/core/sense/a11y-resolver.d.ts +73 -0
  70. package/dist/core/sense/a11y-resolver.js +76 -0
  71. package/dist/core/sense/a11y-resolver.js.map +1 -0
  72. package/dist/core/sense/fingerprint.d.ts +41 -0
  73. package/dist/core/sense/fingerprint.js +123 -0
  74. package/dist/core/sense/fingerprint.js.map +1 -0
  75. package/dist/core/sense/rank.d.ts +70 -0
  76. package/dist/core/sense/rank.js +192 -0
  77. package/dist/core/sense/rank.js.map +1 -0
  78. package/dist/core/sense/reactive-check.d.ts +40 -0
  79. package/dist/core/sense/reactive-check.js +48 -0
  80. package/dist/core/sense/reactive-check.js.map +1 -0
  81. package/dist/core/sense/snapshot.d.ts +19 -0
  82. package/dist/core/sense/snapshot.js +100 -0
  83. package/dist/core/sense/snapshot.js.map +1 -0
  84. package/dist/core/sense/types.d.ts +66 -0
  85. package/dist/core/sense/types.js +9 -0
  86. package/dist/core/sense/types.js.map +1 -0
  87. package/dist/core/sense/ui-map-anchors.d.ts +7 -0
  88. package/dist/core/sense/ui-map-anchors.js +24 -0
  89. package/dist/core/sense/ui-map-anchors.js.map +1 -0
  90. package/dist/core/sense/ui-map-elements.d.ts +5 -0
  91. package/dist/core/sense/ui-map-elements.js +33 -0
  92. package/dist/core/sense/ui-map-elements.js.map +1 -0
  93. package/dist/core/sense/ui-map-find.d.ts +56 -0
  94. package/dist/core/sense/ui-map-find.js +153 -0
  95. package/dist/core/sense/ui-map-find.js.map +1 -0
  96. package/dist/core/sense/ui-map-fuse.d.ts +4 -0
  97. package/dist/core/sense/ui-map-fuse.js +44 -0
  98. package/dist/core/sense/ui-map-fuse.js.map +1 -0
  99. package/dist/core/sense/ui-map-geom.d.ts +3 -0
  100. package/dist/core/sense/ui-map-geom.js +16 -0
  101. package/dist/core/sense/ui-map-geom.js.map +1 -0
  102. package/dist/core/sense/ui-map-holder.d.ts +58 -0
  103. package/dist/core/sense/ui-map-holder.js +87 -0
  104. package/dist/core/sense/ui-map-holder.js.map +1 -0
  105. package/dist/core/sense/ui-map-normalize.d.ts +19 -0
  106. package/dist/core/sense/ui-map-normalize.js +65 -0
  107. package/dist/core/sense/ui-map-normalize.js.map +1 -0
  108. package/dist/core/sense/ui-map-render.d.ts +4 -0
  109. package/dist/core/sense/ui-map-render.js +34 -0
  110. package/dist/core/sense/ui-map-render.js.map +1 -0
  111. package/dist/core/sense/ui-map-resolve.d.ts +41 -0
  112. package/dist/core/sense/ui-map-resolve.js +59 -0
  113. package/dist/core/sense/ui-map-resolve.js.map +1 -0
  114. package/dist/core/sense/ui-map-types.d.ts +66 -0
  115. package/dist/core/sense/ui-map-types.js +11 -0
  116. package/dist/core/sense/ui-map-types.js.map +1 -0
  117. package/dist/core/sense/ui-map.d.ts +29 -0
  118. package/dist/core/sense/ui-map.js +113 -0
  119. package/dist/core/sense/ui-map.js.map +1 -0
  120. package/dist/core/verify/assertions.d.ts +132 -0
  121. package/dist/core/verify/assertions.js +284 -0
  122. package/dist/core/verify/assertions.js.map +1 -0
  123. package/dist/index.d.ts +21 -0
  124. package/dist/index.js +24 -0
  125. package/dist/index.js.map +1 -0
  126. package/dist/llm/browser-config.d.ts +36 -0
  127. package/dist/llm/browser-config.js +83 -0
  128. package/dist/llm/browser-config.js.map +1 -0
  129. package/dist/llm/client.d.ts +268 -0
  130. package/dist/llm/client.js +1094 -0
  131. package/dist/llm/client.js.map +1 -0
  132. package/dist/llm/config.d.ts +79 -0
  133. package/dist/llm/config.js +375 -0
  134. package/dist/llm/config.js.map +1 -0
  135. package/dist/llm/credentials.d.ts +35 -0
  136. package/dist/llm/credentials.js +491 -0
  137. package/dist/llm/credentials.js.map +1 -0
  138. package/dist/llm/external-creds.d.ts +42 -0
  139. package/dist/llm/external-creds.js +169 -0
  140. package/dist/llm/external-creds.js.map +1 -0
  141. package/dist/llm/providers.d.ts +123 -0
  142. package/dist/llm/providers.js +717 -0
  143. package/dist/llm/providers.js.map +1 -0
  144. package/dist/paths.d.ts +31 -0
  145. package/dist/paths.js +147 -0
  146. package/dist/paths.js.map +1 -0
  147. package/dist/platform/accessibility.d.ts +139 -0
  148. package/dist/platform/accessibility.js +670 -0
  149. package/dist/platform/accessibility.js.map +1 -0
  150. package/dist/platform/cdp-driver.d.ts +318 -0
  151. package/dist/platform/cdp-driver.js +1179 -0
  152. package/dist/platform/cdp-driver.js.map +1 -0
  153. package/dist/platform/index.d.ts +11 -0
  154. package/dist/platform/index.js +69 -0
  155. package/dist/platform/index.js.map +1 -0
  156. package/dist/platform/keys.d.ts +17 -0
  157. package/dist/platform/keys.js +129 -0
  158. package/dist/platform/keys.js.map +1 -0
  159. package/dist/platform/launch-poll.d.ts +101 -0
  160. package/dist/platform/launch-poll.js +177 -0
  161. package/dist/platform/launch-poll.js.map +1 -0
  162. package/dist/platform/linux.d.ts +173 -0
  163. package/dist/platform/linux.js +1253 -0
  164. package/dist/platform/linux.js.map +1 -0
  165. package/dist/platform/macos.d.ts +136 -0
  166. package/dist/platform/macos.js +976 -0
  167. package/dist/platform/macos.js.map +1 -0
  168. package/dist/platform/native-desktop.d.ts +145 -0
  169. package/dist/platform/native-desktop.js +936 -0
  170. package/dist/platform/native-desktop.js.map +1 -0
  171. package/dist/platform/native-helper.d.ts +130 -0
  172. package/dist/platform/native-helper.js +592 -0
  173. package/dist/platform/native-helper.js.map +1 -0
  174. package/dist/platform/ocr-engine.d.ts +78 -0
  175. package/dist/platform/ocr-engine.js +363 -0
  176. package/dist/platform/ocr-engine.js.map +1 -0
  177. package/dist/platform/ps-runner.d.ts +28 -0
  178. package/dist/platform/ps-runner.js +228 -0
  179. package/dist/platform/ps-runner.js.map +1 -0
  180. package/dist/platform/types.d.ts +397 -0
  181. package/dist/platform/types.js +15 -0
  182. package/dist/platform/types.js.map +1 -0
  183. package/dist/platform/uri-handler.d.ts +75 -0
  184. package/dist/platform/uri-handler.js +273 -0
  185. package/dist/platform/uri-handler.js.map +1 -0
  186. package/dist/platform/wayland-backend.d.ts +53 -0
  187. package/dist/platform/wayland-backend.js +348 -0
  188. package/dist/platform/wayland-backend.js.map +1 -0
  189. package/dist/platform/windows.d.ts +232 -0
  190. package/dist/platform/windows.js +1210 -0
  191. package/dist/platform/windows.js.map +1 -0
  192. package/dist/postbuild.d.ts +10 -0
  193. package/dist/postbuild.js +98 -0
  194. package/dist/postbuild.js.map +1 -0
  195. package/dist/schema/snapshot.d.ts +33 -0
  196. package/dist/schema/snapshot.js +90 -0
  197. package/dist/schema/snapshot.js.map +1 -0
  198. package/dist/shortcuts.d.ts +30 -0
  199. package/dist/shortcuts.js +261 -0
  200. package/dist/shortcuts.js.map +1 -0
  201. package/dist/surface/cli.d.ts +7 -0
  202. package/dist/surface/cli.js +1556 -0
  203. package/dist/surface/cli.js.map +1 -0
  204. package/dist/surface/dashboard.d.ts +8 -0
  205. package/dist/surface/dashboard.js +1193 -0
  206. package/dist/surface/dashboard.js.map +1 -0
  207. package/dist/surface/doctor.d.ts +29 -0
  208. package/dist/surface/doctor.js +1514 -0
  209. package/dist/surface/doctor.js.map +1 -0
  210. package/dist/surface/format.d.ts +10 -0
  211. package/dist/surface/format.js +37 -0
  212. package/dist/surface/format.js.map +1 -0
  213. package/dist/surface/http-utility.d.ts +65 -0
  214. package/dist/surface/http-utility.js +336 -0
  215. package/dist/surface/http-utility.js.map +1 -0
  216. package/dist/surface/mcp-server.d.ts +91 -0
  217. package/dist/surface/mcp-server.js +280 -0
  218. package/dist/surface/mcp-server.js.map +1 -0
  219. package/dist/surface/onboarding.d.ts +15 -0
  220. package/dist/surface/onboarding.js +184 -0
  221. package/dist/surface/onboarding.js.map +1 -0
  222. package/dist/surface/pidfile.d.ts +79 -0
  223. package/dist/surface/pidfile.js +263 -0
  224. package/dist/surface/pidfile.js.map +1 -0
  225. package/dist/surface/readiness.d.ts +45 -0
  226. package/dist/surface/readiness.js +230 -0
  227. package/dist/surface/readiness.js.map +1 -0
  228. package/dist/surface/report.d.ts +68 -0
  229. package/dist/surface/report.js +341 -0
  230. package/dist/surface/report.js.map +1 -0
  231. package/dist/surface/skill-register.d.ts +14 -0
  232. package/dist/surface/skill-register.js +150 -0
  233. package/dist/surface/skill-register.js.map +1 -0
  234. package/dist/surface/version.d.ts +6 -0
  235. package/dist/surface/version.js +27 -0
  236. package/dist/surface/version.js.map +1 -0
  237. package/dist/tools/a11y.d.ts +8 -0
  238. package/dist/tools/a11y.js +545 -0
  239. package/dist/tools/a11y.js.map +1 -0
  240. package/dist/tools/a11y_depth.d.ts +19 -0
  241. package/dist/tools/a11y_depth.js +455 -0
  242. package/dist/tools/a11y_depth.js.map +1 -0
  243. package/dist/tools/agent.d.ts +15 -0
  244. package/dist/tools/agent.js +248 -0
  245. package/dist/tools/agent.js.map +1 -0
  246. package/dist/tools/batch.d.ts +46 -0
  247. package/dist/tools/batch.js +230 -0
  248. package/dist/tools/batch.js.map +1 -0
  249. package/dist/tools/cdp.d.ts +8 -0
  250. package/dist/tools/cdp.js +233 -0
  251. package/dist/tools/cdp.js.map +1 -0
  252. package/dist/tools/compact.d.ts +63 -0
  253. package/dist/tools/compact.js +418 -0
  254. package/dist/tools/compact.js.map +1 -0
  255. package/dist/tools/cost-class.d.ts +38 -0
  256. package/dist/tools/cost-class.js +117 -0
  257. package/dist/tools/cost-class.js.map +1 -0
  258. package/dist/tools/desktop.d.ts +9 -0
  259. package/dist/tools/desktop.js +346 -0
  260. package/dist/tools/desktop.js.map +1 -0
  261. package/dist/tools/electron_bridge.d.ts +41 -0
  262. package/dist/tools/electron_bridge.js +261 -0
  263. package/dist/tools/electron_bridge.js.map +1 -0
  264. package/dist/tools/extras.d.ts +22 -0
  265. package/dist/tools/extras.js +942 -0
  266. package/dist/tools/extras.js.map +1 -0
  267. package/dist/tools/favorites.d.ts +13 -0
  268. package/dist/tools/favorites.js +137 -0
  269. package/dist/tools/favorites.js.map +1 -0
  270. package/dist/tools/introspection.d.ts +13 -0
  271. package/dist/tools/introspection.js +55 -0
  272. package/dist/tools/introspection.js.map +1 -0
  273. package/dist/tools/ocr.d.ts +8 -0
  274. package/dist/tools/ocr.js +66 -0
  275. package/dist/tools/ocr.js.map +1 -0
  276. package/dist/tools/orchestration.d.ts +7 -0
  277. package/dist/tools/orchestration.js +377 -0
  278. package/dist/tools/orchestration.js.map +1 -0
  279. package/dist/tools/playbooks/extract-compose.d.ts +22 -0
  280. package/dist/tools/playbooks/extract-compose.js +85 -0
  281. package/dist/tools/playbooks/extract-compose.js.map +1 -0
  282. package/dist/tools/playbooks/find-replace.d.ts +11 -0
  283. package/dist/tools/playbooks/find-replace.js +56 -0
  284. package/dist/tools/playbooks/find-replace.js.map +1 -0
  285. package/dist/tools/playbooks/index.d.ts +63 -0
  286. package/dist/tools/playbooks/index.js +70 -0
  287. package/dist/tools/playbooks/index.js.map +1 -0
  288. package/dist/tools/playbooks/keys-blocklist.d.ts +24 -0
  289. package/dist/tools/playbooks/keys-blocklist.js +89 -0
  290. package/dist/tools/playbooks/keys-blocklist.js.map +1 -0
  291. package/dist/tools/registry.d.ts +40 -0
  292. package/dist/tools/registry.js +560 -0
  293. package/dist/tools/registry.js.map +1 -0
  294. package/dist/tools/safety-gate.d.ts +16 -0
  295. package/dist/tools/safety-gate.js +70 -0
  296. package/dist/tools/safety-gate.js.map +1 -0
  297. package/dist/tools/scheduler.d.ts +76 -0
  298. package/dist/tools/scheduler.js +413 -0
  299. package/dist/tools/scheduler.js.map +1 -0
  300. package/dist/tools/shortcuts.d.ts +13 -0
  301. package/dist/tools/shortcuts.js +205 -0
  302. package/dist/tools/shortcuts.js.map +1 -0
  303. package/dist/tools/smart.d.ts +15 -0
  304. package/dist/tools/smart.js +785 -0
  305. package/dist/tools/smart.js.map +1 -0
  306. package/dist/tools/types.d.ts +174 -0
  307. package/dist/tools/types.js +67 -0
  308. package/dist/tools/types.js.map +1 -0
  309. package/dist/tools/window-text.d.ts +15 -0
  310. package/dist/tools/window-text.js +39 -0
  311. package/dist/tools/window-text.js.map +1 -0
  312. package/dist/types.d.ts +122 -0
  313. package/dist/types.js +41 -0
  314. package/dist/types.js.map +1 -0
  315. package/native/Package.swift +38 -0
  316. package/native/README.md +113 -0
  317. package/native/Sources/ClawdCursorHelper/main.swift +602 -0
  318. package/native/Sources/ClawdCursorHost/main.swift +182 -0
  319. package/native/Sources/PermissionCheck/main.swift +53 -0
  320. package/native/Sources/ScreenshotHelper/main.swift +219 -0
  321. package/native/build.sh +139 -0
  322. package/native/entitlements.plist +12 -0
  323. package/package.json +115 -0
  324. package/scripts/banner.ps1 +112 -0
  325. package/scripts/coord-accuracy.ps1 +140 -0
  326. package/scripts/coord-uwp.ps1 +80 -0
  327. package/scripts/edge-glow.ps1 +180 -0
  328. package/scripts/find-element.ps1 +198 -0
  329. package/scripts/get-foreground-window.ps1 +71 -0
  330. package/scripts/get-screen-context.ps1 +183 -0
  331. package/scripts/get-windows.ps1 +66 -0
  332. package/scripts/install-panic-hotkey.ps1 +46 -0
  333. package/scripts/interact-element.ps1 +431 -0
  334. package/scripts/invoke-element.ps1 +314 -0
  335. package/scripts/linux/atspi-bridge.py +356 -0
  336. package/scripts/linux/ocr-recognize.py +154 -0
  337. package/scripts/mac/_window-picker.jxa +163 -0
  338. package/scripts/mac/find-element.jxa +0 -0
  339. package/scripts/mac/find-element.sh +161 -0
  340. package/scripts/mac/focus-window.jxa +284 -0
  341. package/scripts/mac/get-focused-element.jxa +102 -0
  342. package/scripts/mac/get-foreground-window.jxa +173 -0
  343. package/scripts/mac/get-screen-context.jxa +197 -0
  344. package/scripts/mac/get-ui-tree.sh +141 -0
  345. package/scripts/mac/get-windows.jxa +117 -0
  346. package/scripts/mac/interact-element.sh +235 -0
  347. package/scripts/mac/invoke-element.jxa +408 -0
  348. package/scripts/mac/ocr-recognize.swift +124 -0
  349. package/scripts/ocr-recognize.ps1 +102 -0
  350. package/scripts/postinstall-native.js +48 -0
  351. package/scripts/ps-bridge.ps1 +830 -0
  352. package/scripts/smoke-mcp.ps1 +119 -0
  353. package/scripts/sync-version.ts +178 -0
  354. package/scripts/verify-install.js +81 -0
package/package.json ADDED
@@ -0,0 +1,115 @@
1
+ {
2
+ "name": "@mseep/clawdcursor",
3
+ "version": "1.5.5",
4
+ "mcpName": "io.github.AmrDab/clawdcursor",
5
+ "description": "Local MCP server that gives any AI agent safe cross-OS desktop control \u2014 the fallback execution layer for when APIs, CLIs, and direct integrations aren't available. Works with any tool-calling model (Claude, GPT, Gemini, Llama) on Windows, macOS, and Linux. Open source (MIT).",
6
+ "homepage": "https://clawdcursor.com",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "git+https://github.com/AmrDab/clawdcursor.git"
10
+ },
11
+ "bugs": {
12
+ "url": "https://github.com/AmrDab/clawdcursor/issues"
13
+ },
14
+ "author": "Amr Dabbas",
15
+ "keywords": [
16
+ "agent",
17
+ "ai-agent",
18
+ "mcp",
19
+ "mcp-server",
20
+ "model-context-protocol",
21
+ "desktop-automation",
22
+ "computer-use",
23
+ "gui-automation",
24
+ "accessibility",
25
+ "claude",
26
+ "gpt",
27
+ "openai",
28
+ "anthropic",
29
+ "skill",
30
+ "cli",
31
+ "mseep"
32
+ ],
33
+ "bin": {
34
+ "clawdcursor": "dist/surface/cli.js"
35
+ },
36
+ "files": [
37
+ "dist/",
38
+ "scripts/verify-install.js",
39
+ "scripts/postinstall-native.js",
40
+ "scripts/sync-version.ts",
41
+ "scripts/*.ps1",
42
+ "scripts/mac/",
43
+ "scripts/linux/",
44
+ "native/build.sh",
45
+ "native/Package.swift",
46
+ "native/Sources/",
47
+ "native/entitlements.plist",
48
+ "README.md",
49
+ "LICENSE",
50
+ "CHANGELOG.md",
51
+ "SECURITY.md",
52
+ "SKILL.md"
53
+ ],
54
+ "scripts": {
55
+ "postinstall": "node scripts/verify-install.js || node -e \"process.exit(0)\" && node scripts/postinstall-native.js || node -e \"process.exit(0)\"",
56
+ "clean": "node -e \"require('fs').rmSync('dist',{recursive:true,force:true})\"",
57
+ "prepare": "npm run clean && tsc && node dist/postbuild.js",
58
+ "prebuild": "npm run clean",
59
+ "build": "tsc && node dist/postbuild.js",
60
+ "setup": "node scripts/verify-install.js && npm run build && node -e \"if(process.platform==='darwin'){const{execSync}=require('child_process');try{execSync('chmod +x native/build.sh && native/build.sh',{stdio:'inherit'})}catch(e){console.error('\\n\u274c Native build failed. Run manually: cd native && ./build.sh');process.exit(1)}}\" && npm link --force",
61
+ "postbuild:msg": "handled by postbuild.js",
62
+ "dev": "tsx watch src/surface/cli.ts",
63
+ "start": "node dist/surface/cli.js agent",
64
+ "stop": "node dist/surface/cli.js stop",
65
+ "doctor": "node dist/surface/cli.js doctor",
66
+ "lint": "eslint src/",
67
+ "test": "vitest",
68
+ "test:ci": "vitest run",
69
+ "test:mcp-schema-snapshot": "tsx scripts/build-mcp-schema.ts",
70
+ "test:mcp-schema-snapshot:update": "tsx scripts/build-mcp-schema.ts --write",
71
+ "typecheck": "tsc --noEmit",
72
+ "typecheck:tests": "tsc --noEmit -p tsconfig.tests.json",
73
+ "sync-version": "tsx scripts/sync-version.ts",
74
+ "version": "tsx scripts/sync-version.ts && git add SKILL.md docs/index.html docs/install.sh docs/install.ps1",
75
+ "test:coverage": "vitest run --coverage",
76
+ "perf:smoke": "tsx scripts/perf-smoke.ts"
77
+ },
78
+ "dependencies": {
79
+ "@modelcontextprotocol/sdk": "^1.27.1",
80
+ "@nut-tree-fork/nut-js": "^4.2.0",
81
+ "commander": "^15.0.0",
82
+ "croner": "^10.0.1",
83
+ "dotenv": "^17.4.2",
84
+ "express": "^5.2.1",
85
+ "picocolors": "^1.1.1",
86
+ "playwright": "^1.60.0",
87
+ "sharp": "^0.34.5",
88
+ "ws": "^8.21.0",
89
+ "zod": "^4.4.3"
90
+ },
91
+ "devDependencies": {
92
+ "@eslint/js": "^10.0.1",
93
+ "@types/express": "^5.0.6",
94
+ "@types/node": "^25.9.2",
95
+ "@types/supertest": "^7.2.0",
96
+ "@types/ws": "^8.5.0",
97
+ "@typescript-eslint/eslint-plugin": "^8.60.1",
98
+ "@typescript-eslint/parser": "^8.60.1",
99
+ "@vitest/coverage-v8": "^4.1.8",
100
+ "eslint": "^10.4.1",
101
+ "supertest": "^7.2.2",
102
+ "tsx": "^4.22.4",
103
+ "typescript": "^6.0.3",
104
+ "vitest": "^4.1.8"
105
+ },
106
+ "overrides": {
107
+ "ip-address": "10.1.1",
108
+ "brace-expansion": "5.0.6"
109
+ },
110
+ "engines": {
111
+ "node": ">=20.0.0"
112
+ },
113
+ "license": "MIT",
114
+ "publisher": "mseep"
115
+ }
@@ -0,0 +1,112 @@
1
+ # banner.ps1 — "ClawdCursor — desktop control in progress" indicator.
2
+ #
3
+ # A small, always-on-top, borderless pill at the top-center of the primary
4
+ # screen with a blinking red "recording" dot. Shown by the daemon while an
5
+ # agent is actively driving the desktop, so a human at the machine ALWAYS
6
+ # knows automation is live (transparency requirement — same spirit as the
7
+ # labeled agent browser window).
8
+ #
9
+ # Interaction contract with the parent (src/core/banner.ts):
10
+ # - Parent spawns: powershell -NoProfile -ExecutionPolicy Bypass -WindowStyle Hidden -File banner.ps1
11
+ # - DOUBLE-CLICK anywhere on the banner → prints "BANNER_STOP" on stdout and
12
+ # exits; the parent reacts by running the `clawdcursor stop` flow
13
+ # (abort in-flight task + graceful daemon shutdown).
14
+ # - Parent kills this process to hide the banner.
15
+ #
16
+ # CRITICAL: the window must NEVER steal focus — it appears WHILE the agent is
17
+ # typing/clicking, and an activating window would redirect those keystrokes
18
+ # (the exact focus-theft bug class fixed in v1.5.1). WS_EX_NOACTIVATE +
19
+ # ShowWithoutActivation make it a zero-focus overlay; clicks still reach it.
20
+
21
+ $ErrorActionPreference = 'Stop'
22
+ Add-Type -AssemblyName System.Windows.Forms
23
+ Add-Type -AssemblyName System.Drawing
24
+
25
+ Add-Type -ReferencedAssemblies System.Windows.Forms, System.Drawing -TypeDefinition @'
26
+ using System;
27
+ using System.Windows.Forms;
28
+
29
+ public class NoActivateBannerForm : Form {
30
+ // Never take focus when shown…
31
+ protected override bool ShowWithoutActivation { get { return true; } }
32
+ // …or when clicked. WS_EX_NOACTIVATE (0x08000000) + WS_EX_TOPMOST (0x8).
33
+ protected override CreateParams CreateParams {
34
+ get {
35
+ CreateParams p = base.CreateParams;
36
+ p.ExStyle |= 0x08000000; // WS_EX_NOACTIVATE
37
+ p.ExStyle |= 0x00000008; // WS_EX_TOPMOST
38
+ return p;
39
+ }
40
+ }
41
+ }
42
+ '@
43
+
44
+ $form = New-Object NoActivateBannerForm
45
+ $form.FormBorderStyle = 'None'
46
+ $form.ShowInTaskbar = $false
47
+ $form.TopMost = $true
48
+ $form.StartPosition = 'Manual'
49
+ $form.BackColor = [System.Drawing.Color]::FromArgb(18, 18, 26)
50
+ $form.Size = New-Object System.Drawing.Size(430, 40)
51
+
52
+ # Top-center of the primary screen, 8px below the edge.
53
+ $screen = [System.Windows.Forms.Screen]::PrimaryScreen.WorkingArea
54
+ $form.Location = New-Object System.Drawing.Point(
55
+ [int]($screen.X + ($screen.Width - $form.Width) / 2), [int]($screen.Y + 8))
56
+
57
+ # Rounded pill region.
58
+ $gp = New-Object System.Drawing.Drawing2D.GraphicsPath
59
+ $r = 18; $w = $form.Width; $h = $form.Height
60
+ $gp.AddArc(0, 0, $r*2, $r*2, 180, 90)
61
+ $gp.AddArc($w - $r*2, 0, $r*2, $r*2, 270, 90)
62
+ $gp.AddArc($w - $r*2, $h - $r*2, $r*2, $r*2, 0, 90)
63
+ $gp.AddArc(0, $h - $r*2, $r*2, $r*2, 90, 90)
64
+ $gp.CloseFigure()
65
+ $form.Region = New-Object System.Drawing.Region($gp)
66
+
67
+ $dot = New-Object System.Windows.Forms.Label
68
+ $dot.Text = [char]0x25CF # ●
69
+ $dot.Font = New-Object System.Drawing.Font('Segoe UI', 14, [System.Drawing.FontStyle]::Bold)
70
+ $dot.ForeColor = [System.Drawing.Color]::FromArgb(255, 59, 48)
71
+ $dot.BackColor = [System.Drawing.Color]::Transparent
72
+ $dot.AutoSize = $true
73
+ $dot.Location = New-Object System.Drawing.Point(14, 5)
74
+
75
+ $text = New-Object System.Windows.Forms.Label
76
+ $text.Text = 'ClawdCursor ' # brand
77
+ $text.Font = New-Object System.Drawing.Font('Segoe UI Semibold', 10)
78
+ $text.ForeColor = [System.Drawing.Color]::White
79
+ $text.BackColor = [System.Drawing.Color]::Transparent
80
+ $text.AutoSize = $true
81
+ $text.Location = New-Object System.Drawing.Point(40, 10)
82
+
83
+ $sub = New-Object System.Windows.Forms.Label
84
+ $sub.Text = 'desktop control in progress - double-click to stop'
85
+ $sub.Font = New-Object System.Drawing.Font('Segoe UI', 9)
86
+ $sub.ForeColor = [System.Drawing.Color]::FromArgb(170, 170, 185)
87
+ $sub.BackColor = [System.Drawing.Color]::Transparent
88
+ $sub.AutoSize = $true
89
+ $sub.Location = New-Object System.Drawing.Point(133, 11)
90
+
91
+ $form.Controls.AddRange(@($dot, $text, $sub))
92
+
93
+ # Recording blink — bright/dim every 600ms.
94
+ $timer = New-Object System.Windows.Forms.Timer
95
+ $timer.Interval = 600
96
+ $bright = $true
97
+ $timer.Add_Tick({
98
+ $script:bright = -not $script:bright
99
+ if ($script:bright) { $dot.ForeColor = [System.Drawing.Color]::FromArgb(255, 59, 48) }
100
+ else { $dot.ForeColor = [System.Drawing.Color]::FromArgb(110, 25, 20) }
101
+ })
102
+ $timer.Start()
103
+
104
+ $stopHandler = {
105
+ [Console]::Out.WriteLine('BANNER_STOP')
106
+ [Console]::Out.Flush()
107
+ $form.Close()
108
+ }
109
+ $form.Add_MouseDoubleClick($stopHandler)
110
+ foreach ($c in @($dot, $text, $sub)) { $c.Add_MouseDoubleClick($stopHandler) }
111
+
112
+ [System.Windows.Forms.Application]::Run($form)
@@ -0,0 +1,140 @@
1
+ # Direct-drive coordinate accuracy test against the running daemon.
2
+ # No `clawdcursor agent` loop, no LLM in the path - just MCP tool calls.
3
+ #
4
+ # What it proves:
5
+ # 1. read_screen reports element bounds at logical coords.
6
+ # 2. mouse_click on those coords lands on the element (focus shifts to it).
7
+ # 3. invoke_element clicks the right thing without coord math at all.
8
+ # 4. The values returned by find_element + the get_screen_size scale factor
9
+ # agree end-to-end across DPI.
10
+
11
+ param([int]$Port = 3847)
12
+
13
+ $token = (Get-Content "$env:USERPROFILE\.clawdcursor\token" -Raw).Trim()
14
+ $base = "http://127.0.0.1:$Port/mcp"
15
+ $hdrs = @{
16
+ "Authorization" = "Bearer $token"
17
+ "Accept" = "application/json, text/event-stream"
18
+ }
19
+
20
+ function Call-Tool {
21
+ param([string]$Name, [hashtable]$Arguments = @{})
22
+ $body = (@{ jsonrpc="2.0"; id=1; method="tools/call"; params=@{ name=$Name; arguments=$Arguments } } | ConvertTo-Json -Depth 10 -Compress)
23
+ try {
24
+ $r = Invoke-WebRequest -Uri $base -Method POST -Headers $hdrs -ContentType "application/json" -Body $body -UseBasicParsing -TimeoutSec 15
25
+ if ($r.Content -match '(?s)data:\s*(\{.*\})') {
26
+ return ($matches[1] | ConvertFrom-Json)
27
+ }
28
+ return ($r.Content | ConvertFrom-Json)
29
+ } catch { return @{ error = @{ message = $_.Exception.Message } } }
30
+ }
31
+
32
+ function Tool-Text {
33
+ param($r)
34
+ if ($r.error) { return "ERROR: $($r.error.message)" }
35
+ if ($r.result.content) { return ($r.result.content | ForEach-Object { $_.text }) -join "`n" }
36
+ return ($r | ConvertTo-Json -Depth 5 -Compress)
37
+ }
38
+
39
+ Write-Host ""
40
+ Write-Host "============================================"
41
+ Write-Host " Coordinate accuracy test"
42
+ Write-Host "============================================"
43
+
44
+ # A) Screen / scale baseline
45
+ $ssR = Call-Tool "get_screen_size"
46
+ $ss = Tool-Text $ssR
47
+ Write-Host ""
48
+ Write-Host "[A] get_screen_size:"
49
+ Write-Host " $ss"
50
+
51
+ # B) Open Notepad fresh (clean Win32, no ApplicationFrameHost UWP wrapper)
52
+ $null = Call-Tool "open_app" @{ name = "Notepad" }
53
+ Start-Sleep -Seconds 2
54
+ $null = Call-Tool "focus_window" @{ title = "Notepad" }
55
+ Start-Sleep -Milliseconds 500
56
+
57
+ # C) Read the a11y tree, find the Text editor element bounds
58
+ $treeR = Call-Tool "read_screen"
59
+ $tree = Tool-Text $treeR
60
+ Write-Host ""
61
+ Write-Host "[B] Notepad a11y bounds (look for [Edit] / Text editor lines):"
62
+ $tree -split "`n" | Where-Object { $_ -match "Text editor|Edit|Document" } | Select-Object -First 5 | ForEach-Object { Write-Host " $_" }
63
+
64
+ # Extract the Edit bounds. Format example:
65
+ # [Edit] "Text editor" id:... @122,200 1200x600
66
+ $editLine = ($tree -split "`n") | Where-Object { $_ -match "@\d+,\d+\s+\d+x\d+.*Text editor|Text editor.*@\d+,\d+" } | Select-Object -First 1
67
+ if (-not $editLine) {
68
+ $editLine = ($tree -split "`n") | Where-Object { $_ -match "\[Edit\]" } | Select-Object -First 1
69
+ }
70
+ Write-Host ""
71
+ Write-Host "[C] Picked editor line:"
72
+ Write-Host " $editLine"
73
+
74
+ if ($editLine -match "@(\d+),(\d+)\s+(\d+)x(\d+)") {
75
+ $ex = [int]$matches[1]; $ey = [int]$matches[2]
76
+ $ew = [int]$matches[3]; $eh = [int]$matches[4]
77
+ $cx = $ex + [int]($ew / 2)
78
+ $cy = $ey + [int]($eh / 2)
79
+ Write-Host ""
80
+ Write-Host "[D] Edit center calc: ($cx, $cy) bounds @($ex,$ey) ${ew}x${eh}"
81
+
82
+ # D1) Click via raw coords - verify focus moves to the Edit
83
+ $null = Call-Tool "mouse_click" @{ x = $cx; y = $cy }
84
+ Start-Sleep -Milliseconds 400
85
+ $focusR = Call-Tool "get_focused_element"
86
+ $focus = Tool-Text $focusR
87
+ Write-Host ""
88
+ Write-Host "[E] After mouse_click($cx,$cy) - focused element:"
89
+ Write-Host " $focus"
90
+ $isEditFocused = ($focus -match "Edit|Text editor|Document|RichEdit")
91
+ if ($isEditFocused) {
92
+ Write-Host " PASS raw coordinate click landed in the editor" -ForegroundColor Green
93
+ } else {
94
+ Write-Host " FAIL raw coordinate click did NOT focus the editor" -ForegroundColor Red
95
+ }
96
+
97
+ # D2) Now type a marker - if focus is right, it appears in the doc
98
+ $marker = "COORD_TEST_$([int](Get-Random -Maximum 99999))"
99
+ $null = Call-Tool "type_text" @{ text = $marker }
100
+ Start-Sleep -Milliseconds 400
101
+ $tree2 = Tool-Text (Call-Tool "read_screen")
102
+ $contained = ($tree2 -match [regex]::Escape($marker))
103
+ Write-Host ""
104
+ Write-Host "[F] type_text('$marker') -> tree contains marker: $contained"
105
+ if ($contained) {
106
+ Write-Host " PASS raw click + type was end-to-end correct" -ForegroundColor Green
107
+ } else {
108
+ Write-Host " FAIL marker not found in tree" -ForegroundColor Red
109
+ }
110
+ } else {
111
+ Write-Host " Could not parse bounds from editor line - skipping coord test" -ForegroundColor Yellow
112
+ }
113
+
114
+ # E) Title bar buttons: Minimize sits at a known position on every Win32 window.
115
+ # Use it to test edge-of-window coord accuracy without actually minimizing.
116
+ $minLine = ($tree -split "`n") | Where-Object { $_ -match "\[Button\] `"Minimize" } | Select-Object -First 1
117
+ if ($minLine -match "@(\d+),(\d+)\s+(\d+)x(\d+)") {
118
+ $mx = [int]$matches[1] + [int]([int]$matches[3] / 2)
119
+ $my = [int]$matches[2] + [int]([int]$matches[4] / 2)
120
+ Write-Host ""
121
+ Write-Host "[G] Minimize button center: ($mx, $my) - HOVER ONLY (no click)"
122
+ # Hover, then read the focused window - if hover landed there, the tooltip
123
+ # area would update but the focused element should not (hover != click).
124
+ $null = Call-Tool "mouse_hover" @{ x = $mx; y = $my }
125
+ Start-Sleep -Milliseconds 250
126
+ Write-Host " (hover dispatched; visible verification is the tooltip you may see)"
127
+ }
128
+
129
+ Write-Host ""
130
+ Write-Host "============================================"
131
+ Write-Host " Limitations probed:"
132
+ Write-Host "============================================"
133
+ Write-Host " - DPI scaling: get_screen_size already prints the scaleFactor."
134
+ Write-Host " - UWP offset (Calculator): the wrapped ApplicationFrameHost"
135
+ Write-Host " case is tested separately - this run focuses on plain Win32."
136
+ Write-Host " - Multi-monitor: not exercised here. read_screen reports virtual"
137
+ Write-Host " desktop coords; off-primary-monitor accuracy needs a multi-mon"
138
+ Write-Host " box to validate."
139
+ Write-Host " - Negative coords (left-of-primary monitor): same as above."
140
+ Write-Host ""
@@ -0,0 +1,80 @@
1
+ param([int]$Port = 3847)
2
+
3
+ $token = (Get-Content "$env:USERPROFILE\.clawdcursor\token" -Raw).Trim()
4
+ $base = "http://127.0.0.1:$Port/mcp"
5
+ $hdrs = @{
6
+ "Authorization" = "Bearer $token"
7
+ "Accept" = "application/json, text/event-stream"
8
+ }
9
+
10
+ function Call-Tool {
11
+ param([string]$Name, [hashtable]$Arguments = @{})
12
+ $body = (@{ jsonrpc="2.0"; id=1; method="tools/call"; params=@{ name=$Name; arguments=$Arguments } } | ConvertTo-Json -Depth 10 -Compress)
13
+ try {
14
+ $r = Invoke-WebRequest -Uri $base -Method POST -Headers $hdrs -ContentType "application/json" -Body $body -UseBasicParsing -TimeoutSec 15
15
+ if ($r.Content -match '(?s)data:\s*(\{.*\})') { return ($matches[1] | ConvertFrom-Json) }
16
+ return ($r.Content | ConvertFrom-Json)
17
+ } catch { return @{ error = @{ message = $_.Exception.Message } } }
18
+ }
19
+
20
+ function Tool-Text {
21
+ param($r)
22
+ if ($r.error) { return "ERROR: $($r.error.message)" }
23
+ if ($r.result.content) { return ($r.result.content | ForEach-Object { $_.text }) -join "`n" }
24
+ return ($r | ConvertTo-Json -Depth 5 -Compress)
25
+ }
26
+
27
+ Write-Host ""
28
+ Write-Host "============================================"
29
+ Write-Host " UWP (Calculator + ApplicationFrameHost)"
30
+ Write-Host " Raw coordinate click test - no invoke_element shortcut"
31
+ Write-Host "============================================"
32
+
33
+ $null = Call-Tool "open_app" @{ name = "Calculator" }
34
+ Start-Sleep -Seconds 2
35
+ $null = Call-Tool "focus_window" @{ title = "Calculator" }
36
+ Start-Sleep -Milliseconds 500
37
+
38
+ # Clear the display first via invoke (known good)
39
+ $null = Call-Tool "invoke_element" @{ name = "Clear" }
40
+ Start-Sleep -Milliseconds 250
41
+
42
+ $tree = Tool-Text (Call-Tool "read_screen")
43
+
44
+ # Parse coords for Two, Plus, Three, Equals
45
+ $want = @("Two","Plus","Three","Equals")
46
+ $coords = @{}
47
+ foreach ($n in $want) {
48
+ # Lines look like: [Button] "Two" id:num2Button @171,489 77x51
49
+ $line = ($tree -split "`n") | Where-Object { $_ -match "\[Button\]\s+`"$n`"" } | Select-Object -First 1
50
+ if ($line -match "@(\d+),(\d+)\s+(\d+)x(\d+)") {
51
+ $bx = [int]$matches[1]; $by = [int]$matches[2]
52
+ $bw = [int]$matches[3]; $bh = [int]$matches[4]
53
+ $coords[$n] = @{ x = $bx + [int]($bw/2); y = $by + [int]($bh/2); raw = "$bx,$by ${bw}x$bh" }
54
+ Write-Host "[parse] $n bounds: $($coords[$n].raw) -> center ($($coords[$n].x),$($coords[$n].y))"
55
+ } else {
56
+ Write-Host "[parse] $n NOT FOUND in tree" -ForegroundColor Red
57
+ }
58
+ }
59
+
60
+ Write-Host ""
61
+ Write-Host "[click] Driving 2 + 3 = via mouse_click only (no invoke_element):"
62
+ foreach ($n in $want) {
63
+ if ($coords.ContainsKey($n)) {
64
+ $r = Call-Tool "mouse_click" @{ x = $coords[$n].x; y = $coords[$n].y }
65
+ Write-Host " mouse_click $n @ ($($coords[$n].x),$($coords[$n].y)) -> $(Tool-Text $r)"
66
+ Start-Sleep -Milliseconds 200
67
+ }
68
+ }
69
+
70
+ Start-Sleep -Milliseconds 400
71
+ $treeAfter = Tool-Text (Call-Tool "read_screen")
72
+ if ($treeAfter -match "Display is (\d+)") {
73
+ Write-Host ""
74
+ Write-Host ">>> Display shows: $($matches[1])"
75
+ if ($matches[1] -eq "5") {
76
+ Write-Host " PASS UWP raw-coord click chain produced correct result" -ForegroundColor Green
77
+ } else {
78
+ Write-Host " FAIL UWP raw-coord click chain produced WRONG result" -ForegroundColor Red
79
+ }
80
+ }
@@ -0,0 +1,180 @@
1
+ # edge-glow.ps1 — "task in progress" screen-edge glow.
2
+ #
3
+ # A full-screen, CLICK-THROUGH, never-activating overlay that draws a soft glow
4
+ # on all four screen edges and pulses it dim<->bright while clawdcursor is
5
+ # driving the desktop. Spawned/killed by src/core/banner.ts in lockstep with the
6
+ # control-banner pill (pin() on task start, unpin()/idle to hide) — it runs
7
+ # ALONGSIDE the pill, which keeps the double-click-to-stop affordance.
8
+ #
9
+ # Why a per-pixel-alpha layered window (UpdateLayeredWindow) and not a plain
10
+ # WinForms paint + TransparencyKey: a color key can only knock out ONE exact
11
+ # color, so a soft inward fade can't be made click-through cleanly. Per-pixel
12
+ # alpha gives a real soft glow AND a fully click-through interior in one shot.
13
+ #
14
+ # Extended window styles (mirrors banner.ps1's zero-focus contract, plus
15
+ # click-through — critical: it is up WHILE the agent types/clicks):
16
+ # WS_EX_LAYERED 0x00080000 per-pixel alpha compositing
17
+ # WS_EX_TRANSPARENT 0x00000020 click-through (input falls to the app below)
18
+ # WS_EX_NOACTIVATE 0x08000000 never steals focus (agent keeps typing)
19
+ # WS_EX_TOOLWINDOW 0x00000080 no taskbar / alt-tab entry
20
+ # WS_EX_TOPMOST 0x00000008 stays above normal windows
21
+ #
22
+ # Defaults are production: amber, persistent (runs until the parent kills it).
23
+ # For a manual DEMO, pass -DurationSec so it self-closes, e.g.:
24
+ # powershell -NoProfile -ExecutionPolicy Bypass -File edge-glow.ps1 -DurationSec 12
25
+ #
26
+ # Windows-only today, same as the banner. macOS/Linux overlays are a follow-up.
27
+
28
+ param(
29
+ [string]$Color = '#f59e0b', # glow color (amber — "in progress")
30
+ [int] $Thickness = 80, # px depth the glow fades in from each edge
31
+ [int] $PeriodMs = 1600, # full dim->bright->dim cycle
32
+ [int] $MinAlpha = 60, # dim floor (0-255); lower = dims more
33
+ [int] $PeakAlpha = 200, # per-pixel alpha at the very edge (0-255)
34
+ [int] $DurationSec = 0 # 0 = run until killed; >0 = self-close (demo)
35
+ )
36
+
37
+ $ErrorActionPreference = 'Stop'
38
+ Add-Type -AssemblyName System.Windows.Forms
39
+ Add-Type -AssemblyName System.Drawing
40
+
41
+ Add-Type -ReferencedAssemblies System.Windows.Forms, System.Drawing -TypeDefinition @'
42
+ using System;
43
+ using System.Drawing;
44
+ using System.Runtime.InteropServices;
45
+ using System.Windows.Forms;
46
+
47
+ public class GlowOverlay : Form {
48
+ protected override bool ShowWithoutActivation { get { return true; } }
49
+ protected override CreateParams CreateParams {
50
+ get {
51
+ CreateParams cp = base.CreateParams;
52
+ cp.ExStyle |= 0x00080000; // WS_EX_LAYERED
53
+ cp.ExStyle |= 0x00000020; // WS_EX_TRANSPARENT (click-through)
54
+ cp.ExStyle |= 0x08000000; // WS_EX_NOACTIVATE
55
+ cp.ExStyle |= 0x00000080; // WS_EX_TOOLWINDOW
56
+ cp.ExStyle |= 0x00000008; // WS_EX_TOPMOST
57
+ return cp;
58
+ }
59
+ }
60
+
61
+ [DllImport("user32.dll")] static extern IntPtr GetDC(IntPtr hWnd);
62
+ [DllImport("user32.dll")] static extern int ReleaseDC(IntPtr hWnd, IntPtr hDC);
63
+ [DllImport("gdi32.dll")] static extern IntPtr CreateCompatibleDC(IntPtr hDC);
64
+ [DllImport("gdi32.dll")] static extern bool DeleteDC(IntPtr hdc);
65
+ [DllImport("gdi32.dll")] static extern IntPtr SelectObject(IntPtr hdc, IntPtr h);
66
+ [DllImport("gdi32.dll")] static extern bool DeleteObject(IntPtr ho);
67
+ [DllImport("user32.dll")] static extern bool UpdateLayeredWindow(
68
+ IntPtr hwnd, IntPtr hdcDst, ref POINT pptDst, ref SIZE psize,
69
+ IntPtr hdcSrc, ref POINT pptSrc, int crKey, ref BLENDFUNCTION pblend, int dwFlags);
70
+
71
+ [StructLayout(LayoutKind.Sequential)] public struct POINT { public int X, Y; public POINT(int x,int y){X=x;Y=y;} }
72
+ [StructLayout(LayoutKind.Sequential)] public struct SIZE { public int cx, cy; public SIZE(int x,int y){cx=x;cy=y;} }
73
+ [StructLayout(LayoutKind.Sequential, Pack=1)] public struct BLENDFUNCTION {
74
+ public byte BlendOp, BlendFlags, SourceConstantAlpha, AlphaFormat;
75
+ }
76
+
77
+ IntPtr screenDc, memDc, hBitmap, oldBitmap;
78
+ Bitmap bmp;
79
+
80
+ public void SetBitmap(Bitmap b) {
81
+ bmp = b;
82
+ screenDc = GetDC(IntPtr.Zero);
83
+ memDc = CreateCompatibleDC(screenDc);
84
+ hBitmap = b.GetHbitmap(Color.FromArgb(0)); // ARGB DIB for ULW
85
+ oldBitmap = SelectObject(memDc, hBitmap);
86
+ }
87
+
88
+ // Re-push the same bitmap with a new global alpha → smooth pulse.
89
+ public void Push(byte alpha) {
90
+ if (bmp == null) return;
91
+ SIZE size = new SIZE(bmp.Width, bmp.Height);
92
+ POINT src = new POINT(0, 0);
93
+ POINT dst = new POINT(this.Left, this.Top);
94
+ BLENDFUNCTION blend = new BLENDFUNCTION();
95
+ blend.BlendOp = 0; // AC_SRC_OVER
96
+ blend.BlendFlags = 0;
97
+ blend.SourceConstantAlpha = alpha;
98
+ blend.AlphaFormat = 1; // AC_SRC_ALPHA
99
+ UpdateLayeredWindow(this.Handle, screenDc, ref dst, ref size, memDc, ref src, 0, ref blend, 2); // ULW_ALPHA
100
+ }
101
+
102
+ public void Cleanup() {
103
+ if (memDc != IntPtr.Zero) SelectObject(memDc, oldBitmap);
104
+ if (hBitmap != IntPtr.Zero) { DeleteObject(hBitmap); hBitmap = IntPtr.Zero; }
105
+ if (memDc != IntPtr.Zero) { DeleteDC(memDc); memDc = IntPtr.Zero; }
106
+ if (screenDc != IntPtr.Zero) { ReleaseDC(IntPtr.Zero, screenDc); screenDc = IntPtr.Zero; }
107
+ }
108
+ }
109
+ '@
110
+
111
+ # ── Parse color ──
112
+ $hex = $Color.TrimStart('#')
113
+ $cr = [Convert]::ToInt32($hex.Substring(0,2),16)
114
+ $cg = [Convert]::ToInt32($hex.Substring(2,2),16)
115
+ $cb = [Convert]::ToInt32($hex.Substring(4,2),16)
116
+
117
+ # ── Cover the primary screen ──
118
+ $scr = [System.Windows.Forms.Screen]::PrimaryScreen.Bounds
119
+ $W = $scr.Width
120
+ $H = $scr.Height
121
+ $T = [Math]::Min($Thickness, [Math]::Min([int]($W/2), [int]($H/2)))
122
+
123
+ # ── Render the edge glow into an ARGB bitmap once ──
124
+ $bmp = New-Object System.Drawing.Bitmap($W, $H, [System.Drawing.Imaging.PixelFormat]::Format32bppArgb)
125
+ $g = [System.Drawing.Graphics]::FromImage($bmp)
126
+ $g.SmoothingMode = [System.Drawing.Drawing2D.SmoothingMode]::AntiAlias
127
+ $g.Clear([System.Drawing.Color]::FromArgb(0,0,0,0))
128
+
129
+ $edge = [System.Drawing.Color]::FromArgb($PeakAlpha, $cr, $cg, $cb)
130
+ $fade = [System.Drawing.Color]::FromArgb(0, $cr, $cg, $cb)
131
+ $P = [System.Drawing.Point]
132
+ function New-Band($p1, $p2, $colorAt1, $colorAt2, $rect) {
133
+ $br = New-Object System.Drawing.Drawing2D.LinearGradientBrush($p1, $p2, $colorAt1, $colorAt2)
134
+ $br.WrapMode = [System.Drawing.Drawing2D.WrapMode]::TileFlipXY
135
+ $g.FillRectangle($br, $rect)
136
+ $br.Dispose()
137
+ }
138
+ # Corners deliberately overlap (additive glow pooling).
139
+ New-Band (New-Object $P 0,0) (New-Object $P 0,$T) $edge $fade (New-Object Drawing.Rectangle 0,0,$W,$T) # top
140
+ New-Band (New-Object $P 0,($H-$T)) (New-Object $P 0,$H) $fade $edge (New-Object Drawing.Rectangle 0,($H-$T),$W,$T) # bottom
141
+ New-Band (New-Object $P 0,0) (New-Object $P $T,0) $edge $fade (New-Object Drawing.Rectangle 0,0,$T,$H) # left
142
+ New-Band (New-Object $P ($W-$T),0) (New-Object $P $W,0) $fade $edge (New-Object Drawing.Rectangle ($W-$T),0,$T,$H) # right
143
+ $g.Dispose()
144
+
145
+ # ── Build the overlay window ──
146
+ $form = New-Object GlowOverlay
147
+ $form.FormBorderStyle = 'None'
148
+ $form.ShowInTaskbar = $false
149
+ $form.TopMost = $true
150
+ $form.StartPosition = 'Manual'
151
+ $form.Bounds = $scr
152
+
153
+ $startTicks = [Environment]::TickCount
154
+
155
+ $form.Add_Shown({
156
+ $form.SetBitmap($bmp)
157
+ $form.Push([byte]$MinAlpha)
158
+ })
159
+
160
+ # Pulse: cosine ease between MinAlpha and 255 (smooth dim<->bright).
161
+ $pulse = New-Object System.Windows.Forms.Timer
162
+ $pulse.Interval = 30
163
+ $pulse.Add_Tick({
164
+ $elapsed = [Environment]::TickCount - $startTicks
165
+ if ($DurationSec -gt 0 -and $elapsed -ge ($DurationSec * 1000)) {
166
+ $pulse.Stop()
167
+ $form.Cleanup()
168
+ $form.Close()
169
+ return
170
+ }
171
+ $phase = ($elapsed % $PeriodMs) / [double]$PeriodMs
172
+ $wave = (1 - [Math]::Cos($phase * 2 * [Math]::PI)) / 2 # 0->1->0
173
+ $alpha = [int]($MinAlpha + ($wave * (255 - $MinAlpha)))
174
+ if ($alpha -lt 0) { $alpha = 0 } elseif ($alpha -gt 255) { $alpha = 255 }
175
+ $form.Push([byte]$alpha)
176
+ })
177
+ $pulse.Start()
178
+
179
+ [System.Windows.Forms.Application]::Run($form)
180
+ $bmp.Dispose()