@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.
- package/CHANGELOG.md +2264 -0
- package/LICENSE +21 -0
- package/README.md +385 -0
- package/SECURITY.md +44 -0
- package/SKILL.md +503 -0
- package/dist/core/agent-loop/agent.d.ts +42 -0
- package/dist/core/agent-loop/agent.js +1023 -0
- package/dist/core/agent-loop/agent.js.map +1 -0
- package/dist/core/agent-loop/batch-tool.d.ts +25 -0
- package/dist/core/agent-loop/batch-tool.js +218 -0
- package/dist/core/agent-loop/batch-tool.js.map +1 -0
- package/dist/core/agent-loop/coord-scale.d.ts +72 -0
- package/dist/core/agent-loop/coord-scale.js +89 -0
- package/dist/core/agent-loop/coord-scale.js.map +1 -0
- package/dist/core/agent-loop/focus-guard.d.ts +24 -0
- package/dist/core/agent-loop/focus-guard.js +29 -0
- package/dist/core/agent-loop/focus-guard.js.map +1 -0
- package/dist/core/agent-loop/project-mcp.d.ts +97 -0
- package/dist/core/agent-loop/project-mcp.js +253 -0
- package/dist/core/agent-loop/project-mcp.js.map +1 -0
- package/dist/core/agent-loop/prompt.d.ts +45 -0
- package/dist/core/agent-loop/prompt.js +426 -0
- package/dist/core/agent-loop/prompt.js.map +1 -0
- package/dist/core/agent-loop/tool-meta.d.ts +93 -0
- package/dist/core/agent-loop/tool-meta.js +651 -0
- package/dist/core/agent-loop/tool-meta.js.map +1 -0
- package/dist/core/agent-loop/tools.d.ts +38 -0
- package/dist/core/agent-loop/tools.js +2134 -0
- package/dist/core/agent-loop/tools.js.map +1 -0
- package/dist/core/agent-loop/types.d.ts +170 -0
- package/dist/core/agent-loop/types.js +12 -0
- package/dist/core/agent-loop/types.js.map +1 -0
- package/dist/core/agent.d.ts +51 -0
- package/dist/core/agent.js +245 -0
- package/dist/core/agent.js.map +1 -0
- package/dist/core/app-categories.d.ts +67 -0
- package/dist/core/app-categories.js +108 -0
- package/dist/core/app-categories.js.map +1 -0
- package/dist/core/banner.d.ts +70 -0
- package/dist/core/banner.js +245 -0
- package/dist/core/banner.js.map +1 -0
- package/dist/core/classify/capability.d.ts +45 -0
- package/dist/core/classify/capability.js +78 -0
- package/dist/core/classify/capability.js.map +1 -0
- package/dist/core/decompose/llm-decomposer.d.ts +35 -0
- package/dist/core/decompose/llm-decomposer.js +156 -0
- package/dist/core/decompose/llm-decomposer.js.map +1 -0
- package/dist/core/decompose/parser.d.ts +27 -0
- package/dist/core/decompose/parser.js +101 -0
- package/dist/core/decompose/parser.js.map +1 -0
- package/dist/core/observability/correlation.d.ts +19 -0
- package/dist/core/observability/correlation.js +36 -0
- package/dist/core/observability/correlation.js.map +1 -0
- package/dist/core/observability/cost-meter.d.ts +51 -0
- package/dist/core/observability/cost-meter.js +134 -0
- package/dist/core/observability/cost-meter.js.map +1 -0
- package/dist/core/observability/logger.d.ts +61 -0
- package/dist/core/observability/logger.js +550 -0
- package/dist/core/observability/logger.js.map +1 -0
- package/dist/core/router/aliases.d.ts +50 -0
- package/dist/core/router/aliases.js +104 -0
- package/dist/core/router/aliases.js.map +1 -0
- package/dist/core/router/normalize.d.ts +41 -0
- package/dist/core/router/normalize.js +80 -0
- package/dist/core/router/normalize.js.map +1 -0
- package/dist/core/safety.d.ts +126 -0
- package/dist/core/safety.js +568 -0
- package/dist/core/safety.js.map +1 -0
- package/dist/core/sense/a11y-resolver.d.ts +73 -0
- package/dist/core/sense/a11y-resolver.js +76 -0
- package/dist/core/sense/a11y-resolver.js.map +1 -0
- package/dist/core/sense/fingerprint.d.ts +41 -0
- package/dist/core/sense/fingerprint.js +123 -0
- package/dist/core/sense/fingerprint.js.map +1 -0
- package/dist/core/sense/rank.d.ts +70 -0
- package/dist/core/sense/rank.js +192 -0
- package/dist/core/sense/rank.js.map +1 -0
- package/dist/core/sense/reactive-check.d.ts +40 -0
- package/dist/core/sense/reactive-check.js +48 -0
- package/dist/core/sense/reactive-check.js.map +1 -0
- package/dist/core/sense/snapshot.d.ts +19 -0
- package/dist/core/sense/snapshot.js +100 -0
- package/dist/core/sense/snapshot.js.map +1 -0
- package/dist/core/sense/types.d.ts +66 -0
- package/dist/core/sense/types.js +9 -0
- package/dist/core/sense/types.js.map +1 -0
- package/dist/core/sense/ui-map-anchors.d.ts +7 -0
- package/dist/core/sense/ui-map-anchors.js +24 -0
- package/dist/core/sense/ui-map-anchors.js.map +1 -0
- package/dist/core/sense/ui-map-elements.d.ts +5 -0
- package/dist/core/sense/ui-map-elements.js +33 -0
- package/dist/core/sense/ui-map-elements.js.map +1 -0
- package/dist/core/sense/ui-map-find.d.ts +56 -0
- package/dist/core/sense/ui-map-find.js +153 -0
- package/dist/core/sense/ui-map-find.js.map +1 -0
- package/dist/core/sense/ui-map-fuse.d.ts +4 -0
- package/dist/core/sense/ui-map-fuse.js +44 -0
- package/dist/core/sense/ui-map-fuse.js.map +1 -0
- package/dist/core/sense/ui-map-geom.d.ts +3 -0
- package/dist/core/sense/ui-map-geom.js +16 -0
- package/dist/core/sense/ui-map-geom.js.map +1 -0
- package/dist/core/sense/ui-map-holder.d.ts +58 -0
- package/dist/core/sense/ui-map-holder.js +87 -0
- package/dist/core/sense/ui-map-holder.js.map +1 -0
- package/dist/core/sense/ui-map-normalize.d.ts +19 -0
- package/dist/core/sense/ui-map-normalize.js +65 -0
- package/dist/core/sense/ui-map-normalize.js.map +1 -0
- package/dist/core/sense/ui-map-render.d.ts +4 -0
- package/dist/core/sense/ui-map-render.js +34 -0
- package/dist/core/sense/ui-map-render.js.map +1 -0
- package/dist/core/sense/ui-map-resolve.d.ts +41 -0
- package/dist/core/sense/ui-map-resolve.js +59 -0
- package/dist/core/sense/ui-map-resolve.js.map +1 -0
- package/dist/core/sense/ui-map-types.d.ts +66 -0
- package/dist/core/sense/ui-map-types.js +11 -0
- package/dist/core/sense/ui-map-types.js.map +1 -0
- package/dist/core/sense/ui-map.d.ts +29 -0
- package/dist/core/sense/ui-map.js +113 -0
- package/dist/core/sense/ui-map.js.map +1 -0
- package/dist/core/verify/assertions.d.ts +132 -0
- package/dist/core/verify/assertions.js +284 -0
- package/dist/core/verify/assertions.js.map +1 -0
- package/dist/index.d.ts +21 -0
- package/dist/index.js +24 -0
- package/dist/index.js.map +1 -0
- package/dist/llm/browser-config.d.ts +36 -0
- package/dist/llm/browser-config.js +83 -0
- package/dist/llm/browser-config.js.map +1 -0
- package/dist/llm/client.d.ts +268 -0
- package/dist/llm/client.js +1094 -0
- package/dist/llm/client.js.map +1 -0
- package/dist/llm/config.d.ts +79 -0
- package/dist/llm/config.js +375 -0
- package/dist/llm/config.js.map +1 -0
- package/dist/llm/credentials.d.ts +35 -0
- package/dist/llm/credentials.js +491 -0
- package/dist/llm/credentials.js.map +1 -0
- package/dist/llm/external-creds.d.ts +42 -0
- package/dist/llm/external-creds.js +169 -0
- package/dist/llm/external-creds.js.map +1 -0
- package/dist/llm/providers.d.ts +123 -0
- package/dist/llm/providers.js +717 -0
- package/dist/llm/providers.js.map +1 -0
- package/dist/paths.d.ts +31 -0
- package/dist/paths.js +147 -0
- package/dist/paths.js.map +1 -0
- package/dist/platform/accessibility.d.ts +139 -0
- package/dist/platform/accessibility.js +670 -0
- package/dist/platform/accessibility.js.map +1 -0
- package/dist/platform/cdp-driver.d.ts +318 -0
- package/dist/platform/cdp-driver.js +1179 -0
- package/dist/platform/cdp-driver.js.map +1 -0
- package/dist/platform/index.d.ts +11 -0
- package/dist/platform/index.js +69 -0
- package/dist/platform/index.js.map +1 -0
- package/dist/platform/keys.d.ts +17 -0
- package/dist/platform/keys.js +129 -0
- package/dist/platform/keys.js.map +1 -0
- package/dist/platform/launch-poll.d.ts +101 -0
- package/dist/platform/launch-poll.js +177 -0
- package/dist/platform/launch-poll.js.map +1 -0
- package/dist/platform/linux.d.ts +173 -0
- package/dist/platform/linux.js +1253 -0
- package/dist/platform/linux.js.map +1 -0
- package/dist/platform/macos.d.ts +136 -0
- package/dist/platform/macos.js +976 -0
- package/dist/platform/macos.js.map +1 -0
- package/dist/platform/native-desktop.d.ts +145 -0
- package/dist/platform/native-desktop.js +936 -0
- package/dist/platform/native-desktop.js.map +1 -0
- package/dist/platform/native-helper.d.ts +130 -0
- package/dist/platform/native-helper.js +592 -0
- package/dist/platform/native-helper.js.map +1 -0
- package/dist/platform/ocr-engine.d.ts +78 -0
- package/dist/platform/ocr-engine.js +363 -0
- package/dist/platform/ocr-engine.js.map +1 -0
- package/dist/platform/ps-runner.d.ts +28 -0
- package/dist/platform/ps-runner.js +228 -0
- package/dist/platform/ps-runner.js.map +1 -0
- package/dist/platform/types.d.ts +397 -0
- package/dist/platform/types.js +15 -0
- package/dist/platform/types.js.map +1 -0
- package/dist/platform/uri-handler.d.ts +75 -0
- package/dist/platform/uri-handler.js +273 -0
- package/dist/platform/uri-handler.js.map +1 -0
- package/dist/platform/wayland-backend.d.ts +53 -0
- package/dist/platform/wayland-backend.js +348 -0
- package/dist/platform/wayland-backend.js.map +1 -0
- package/dist/platform/windows.d.ts +232 -0
- package/dist/platform/windows.js +1210 -0
- package/dist/platform/windows.js.map +1 -0
- package/dist/postbuild.d.ts +10 -0
- package/dist/postbuild.js +98 -0
- package/dist/postbuild.js.map +1 -0
- package/dist/schema/snapshot.d.ts +33 -0
- package/dist/schema/snapshot.js +90 -0
- package/dist/schema/snapshot.js.map +1 -0
- package/dist/shortcuts.d.ts +30 -0
- package/dist/shortcuts.js +261 -0
- package/dist/shortcuts.js.map +1 -0
- package/dist/surface/cli.d.ts +7 -0
- package/dist/surface/cli.js +1556 -0
- package/dist/surface/cli.js.map +1 -0
- package/dist/surface/dashboard.d.ts +8 -0
- package/dist/surface/dashboard.js +1193 -0
- package/dist/surface/dashboard.js.map +1 -0
- package/dist/surface/doctor.d.ts +29 -0
- package/dist/surface/doctor.js +1514 -0
- package/dist/surface/doctor.js.map +1 -0
- package/dist/surface/format.d.ts +10 -0
- package/dist/surface/format.js +37 -0
- package/dist/surface/format.js.map +1 -0
- package/dist/surface/http-utility.d.ts +65 -0
- package/dist/surface/http-utility.js +336 -0
- package/dist/surface/http-utility.js.map +1 -0
- package/dist/surface/mcp-server.d.ts +91 -0
- package/dist/surface/mcp-server.js +280 -0
- package/dist/surface/mcp-server.js.map +1 -0
- package/dist/surface/onboarding.d.ts +15 -0
- package/dist/surface/onboarding.js +184 -0
- package/dist/surface/onboarding.js.map +1 -0
- package/dist/surface/pidfile.d.ts +79 -0
- package/dist/surface/pidfile.js +263 -0
- package/dist/surface/pidfile.js.map +1 -0
- package/dist/surface/readiness.d.ts +45 -0
- package/dist/surface/readiness.js +230 -0
- package/dist/surface/readiness.js.map +1 -0
- package/dist/surface/report.d.ts +68 -0
- package/dist/surface/report.js +341 -0
- package/dist/surface/report.js.map +1 -0
- package/dist/surface/skill-register.d.ts +14 -0
- package/dist/surface/skill-register.js +150 -0
- package/dist/surface/skill-register.js.map +1 -0
- package/dist/surface/version.d.ts +6 -0
- package/dist/surface/version.js +27 -0
- package/dist/surface/version.js.map +1 -0
- package/dist/tools/a11y.d.ts +8 -0
- package/dist/tools/a11y.js +545 -0
- package/dist/tools/a11y.js.map +1 -0
- package/dist/tools/a11y_depth.d.ts +19 -0
- package/dist/tools/a11y_depth.js +455 -0
- package/dist/tools/a11y_depth.js.map +1 -0
- package/dist/tools/agent.d.ts +15 -0
- package/dist/tools/agent.js +248 -0
- package/dist/tools/agent.js.map +1 -0
- package/dist/tools/batch.d.ts +46 -0
- package/dist/tools/batch.js +230 -0
- package/dist/tools/batch.js.map +1 -0
- package/dist/tools/cdp.d.ts +8 -0
- package/dist/tools/cdp.js +233 -0
- package/dist/tools/cdp.js.map +1 -0
- package/dist/tools/compact.d.ts +63 -0
- package/dist/tools/compact.js +418 -0
- package/dist/tools/compact.js.map +1 -0
- package/dist/tools/cost-class.d.ts +38 -0
- package/dist/tools/cost-class.js +117 -0
- package/dist/tools/cost-class.js.map +1 -0
- package/dist/tools/desktop.d.ts +9 -0
- package/dist/tools/desktop.js +346 -0
- package/dist/tools/desktop.js.map +1 -0
- package/dist/tools/electron_bridge.d.ts +41 -0
- package/dist/tools/electron_bridge.js +261 -0
- package/dist/tools/electron_bridge.js.map +1 -0
- package/dist/tools/extras.d.ts +22 -0
- package/dist/tools/extras.js +942 -0
- package/dist/tools/extras.js.map +1 -0
- package/dist/tools/favorites.d.ts +13 -0
- package/dist/tools/favorites.js +137 -0
- package/dist/tools/favorites.js.map +1 -0
- package/dist/tools/introspection.d.ts +13 -0
- package/dist/tools/introspection.js +55 -0
- package/dist/tools/introspection.js.map +1 -0
- package/dist/tools/ocr.d.ts +8 -0
- package/dist/tools/ocr.js +66 -0
- package/dist/tools/ocr.js.map +1 -0
- package/dist/tools/orchestration.d.ts +7 -0
- package/dist/tools/orchestration.js +377 -0
- package/dist/tools/orchestration.js.map +1 -0
- package/dist/tools/playbooks/extract-compose.d.ts +22 -0
- package/dist/tools/playbooks/extract-compose.js +85 -0
- package/dist/tools/playbooks/extract-compose.js.map +1 -0
- package/dist/tools/playbooks/find-replace.d.ts +11 -0
- package/dist/tools/playbooks/find-replace.js +56 -0
- package/dist/tools/playbooks/find-replace.js.map +1 -0
- package/dist/tools/playbooks/index.d.ts +63 -0
- package/dist/tools/playbooks/index.js +70 -0
- package/dist/tools/playbooks/index.js.map +1 -0
- package/dist/tools/playbooks/keys-blocklist.d.ts +24 -0
- package/dist/tools/playbooks/keys-blocklist.js +89 -0
- package/dist/tools/playbooks/keys-blocklist.js.map +1 -0
- package/dist/tools/registry.d.ts +40 -0
- package/dist/tools/registry.js +560 -0
- package/dist/tools/registry.js.map +1 -0
- package/dist/tools/safety-gate.d.ts +16 -0
- package/dist/tools/safety-gate.js +70 -0
- package/dist/tools/safety-gate.js.map +1 -0
- package/dist/tools/scheduler.d.ts +76 -0
- package/dist/tools/scheduler.js +413 -0
- package/dist/tools/scheduler.js.map +1 -0
- package/dist/tools/shortcuts.d.ts +13 -0
- package/dist/tools/shortcuts.js +205 -0
- package/dist/tools/shortcuts.js.map +1 -0
- package/dist/tools/smart.d.ts +15 -0
- package/dist/tools/smart.js +785 -0
- package/dist/tools/smart.js.map +1 -0
- package/dist/tools/types.d.ts +174 -0
- package/dist/tools/types.js +67 -0
- package/dist/tools/types.js.map +1 -0
- package/dist/tools/window-text.d.ts +15 -0
- package/dist/tools/window-text.js +39 -0
- package/dist/tools/window-text.js.map +1 -0
- package/dist/types.d.ts +122 -0
- package/dist/types.js +41 -0
- package/dist/types.js.map +1 -0
- package/native/Package.swift +38 -0
- package/native/README.md +113 -0
- package/native/Sources/ClawdCursorHelper/main.swift +602 -0
- package/native/Sources/ClawdCursorHost/main.swift +182 -0
- package/native/Sources/PermissionCheck/main.swift +53 -0
- package/native/Sources/ScreenshotHelper/main.swift +219 -0
- package/native/build.sh +139 -0
- package/native/entitlements.plist +12 -0
- package/package.json +115 -0
- package/scripts/banner.ps1 +112 -0
- package/scripts/coord-accuracy.ps1 +140 -0
- package/scripts/coord-uwp.ps1 +80 -0
- package/scripts/edge-glow.ps1 +180 -0
- package/scripts/find-element.ps1 +198 -0
- package/scripts/get-foreground-window.ps1 +71 -0
- package/scripts/get-screen-context.ps1 +183 -0
- package/scripts/get-windows.ps1 +66 -0
- package/scripts/install-panic-hotkey.ps1 +46 -0
- package/scripts/interact-element.ps1 +431 -0
- package/scripts/invoke-element.ps1 +314 -0
- package/scripts/linux/atspi-bridge.py +356 -0
- package/scripts/linux/ocr-recognize.py +154 -0
- package/scripts/mac/_window-picker.jxa +163 -0
- package/scripts/mac/find-element.jxa +0 -0
- package/scripts/mac/find-element.sh +161 -0
- package/scripts/mac/focus-window.jxa +284 -0
- package/scripts/mac/get-focused-element.jxa +102 -0
- package/scripts/mac/get-foreground-window.jxa +173 -0
- package/scripts/mac/get-screen-context.jxa +197 -0
- package/scripts/mac/get-ui-tree.sh +141 -0
- package/scripts/mac/get-windows.jxa +117 -0
- package/scripts/mac/interact-element.sh +235 -0
- package/scripts/mac/invoke-element.jxa +408 -0
- package/scripts/mac/ocr-recognize.swift +124 -0
- package/scripts/ocr-recognize.ps1 +102 -0
- package/scripts/postinstall-native.js +48 -0
- package/scripts/ps-bridge.ps1 +830 -0
- package/scripts/smoke-mcp.ps1 +119 -0
- package/scripts/sync-version.ts +178 -0
- package/scripts/verify-install.js +81 -0
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
#!/usr/bin/env osascript -l JavaScript
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* focus-window.jxa
|
|
5
|
+
*
|
|
6
|
+
* Brings a window to the front by title substring or process ID.
|
|
7
|
+
*
|
|
8
|
+
* Parameters:
|
|
9
|
+
* -title <string> - Substring match against window titles (case-insensitive)
|
|
10
|
+
* -processId <number> - Process ID to focus
|
|
11
|
+
*
|
|
12
|
+
* Returns JSON result with success status and window info.
|
|
13
|
+
*
|
|
14
|
+
* Note: At least one of -title or -processId must be specified.
|
|
15
|
+
*
|
|
16
|
+
* When BOTH are supplied, -title is used to disambiguate among that
|
|
17
|
+
* process's windows (multi-window apps like Xcode, VS Code, Chrome).
|
|
18
|
+
* The previous implementation always picked process.windows[0], which
|
|
19
|
+
* surfaced whichever popup was most recent (e.g. Xcode's "Downloads"
|
|
20
|
+
* popover at 1760,80 instead of the user's project window). See bug
|
|
21
|
+
* #2 of clawdcursor issue #101.
|
|
22
|
+
*
|
|
23
|
+
* Usage: osascript -l JavaScript focus-window.jxa -title "Safari"
|
|
24
|
+
* osascript -l JavaScript focus-window.jxa -processId 1234
|
|
25
|
+
* osascript -l JavaScript focus-window.jxa -processId 1234 -title "Amr Dabbas"
|
|
26
|
+
*/
|
|
27
|
+
|
|
28
|
+
// ── Inlined from scripts/mac/_window-picker.jxa ───────────────────
|
|
29
|
+
// Keep these helpers in sync with _window-picker.jxa; osascript can't require().
|
|
30
|
+
|
|
31
|
+
function safeGet(obj, property, defaultValue) {
|
|
32
|
+
try {
|
|
33
|
+
var val = obj[property]();
|
|
34
|
+
return (val !== undefined && val !== null) ? val : defaultValue;
|
|
35
|
+
} catch (e) {
|
|
36
|
+
return defaultValue;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function getWinBounds(win) {
|
|
41
|
+
var bounds = { x: 0, y: 0, width: 0, height: 0 };
|
|
42
|
+
try {
|
|
43
|
+
var p = win.position();
|
|
44
|
+
var s = win.size();
|
|
45
|
+
if (p && s) {
|
|
46
|
+
bounds.x = p[0] || 0;
|
|
47
|
+
bounds.y = p[1] || 0;
|
|
48
|
+
bounds.width = s[0] || 0;
|
|
49
|
+
bounds.height = s[1] || 0;
|
|
50
|
+
}
|
|
51
|
+
} catch (e) {}
|
|
52
|
+
return bounds;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function getWinSubrole(win) {
|
|
56
|
+
try { return win.subrole() || ''; } catch (e) {}
|
|
57
|
+
try { return win.attributes.byName('AXSubrole').value() || ''; } catch (e) {}
|
|
58
|
+
return '';
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function isLikelyTrayTitle(title) {
|
|
62
|
+
if (!title) return false;
|
|
63
|
+
var t = String(title).trim().toLowerCase();
|
|
64
|
+
return t === 'downloads' || t === 'notifications' || t === 'updates' ||
|
|
65
|
+
t === 'inspector' || t === 'activity';
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function scoreWindow(win, index) {
|
|
69
|
+
var bounds = getWinBounds(win);
|
|
70
|
+
var area = bounds.width * bounds.height;
|
|
71
|
+
if (area <= 0) return -Infinity;
|
|
72
|
+
var score = area;
|
|
73
|
+
var subrole = getWinSubrole(win);
|
|
74
|
+
if (subrole === 'AXStandardWindow') score += 2000000;
|
|
75
|
+
else if (subrole === 'AXFloatingWindow') score -= 5000000;
|
|
76
|
+
else if (subrole === 'AXDialog' || subrole === 'AXSystemDialog') score -= 3000000;
|
|
77
|
+
var title = '';
|
|
78
|
+
try { title = win.name() || ''; } catch (e) {}
|
|
79
|
+
if (isLikelyTrayTitle(title)) score -= 1500000;
|
|
80
|
+
score += index * 10;
|
|
81
|
+
return score;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function pickInteractionWindow(windows) {
|
|
85
|
+
var best = null, bestScore = -Infinity, bestIndex = -1;
|
|
86
|
+
for (var i = 0; i < windows.length; i++) {
|
|
87
|
+
try {
|
|
88
|
+
var s = scoreWindow(windows[i], i);
|
|
89
|
+
if (s > bestScore) { bestScore = s; best = windows[i]; bestIndex = i; }
|
|
90
|
+
} catch (e) {}
|
|
91
|
+
}
|
|
92
|
+
if (!best) return null;
|
|
93
|
+
return { window: best, index: bestIndex };
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function findTitleMatchWindow(windows, needle) {
|
|
97
|
+
if (!needle) return null;
|
|
98
|
+
var n = String(needle).toLowerCase();
|
|
99
|
+
for (var i = 0; i < windows.length; i++) {
|
|
100
|
+
try {
|
|
101
|
+
var w = windows[i];
|
|
102
|
+
var title = '';
|
|
103
|
+
try { title = w.name() || ''; } catch (e) {}
|
|
104
|
+
if (!title) continue;
|
|
105
|
+
if (title.toLowerCase().indexOf(n) === -1) continue;
|
|
106
|
+
var b = getWinBounds(w);
|
|
107
|
+
if (b.width * b.height <= 0) continue;
|
|
108
|
+
return { window: w, index: i, title: title };
|
|
109
|
+
} catch (e) {}
|
|
110
|
+
}
|
|
111
|
+
return null;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// ── End inlined helpers ───────────────────────────────────────────
|
|
115
|
+
|
|
116
|
+
// run(argv) is the JXA entry point — osascript calls it and its return value becomes stdout.
|
|
117
|
+
// argv receives command-line arguments passed after the script path.
|
|
118
|
+
function run(argv) {
|
|
119
|
+
try {
|
|
120
|
+
// Parse arguments from argv array
|
|
121
|
+
var params = { title: null, processId: null };
|
|
122
|
+
for (var i = 0; i < argv.length; i++) {
|
|
123
|
+
var a = argv[i];
|
|
124
|
+
var hasNext = i + 1 < argv.length;
|
|
125
|
+
if ((a === '-title' || a === '-Title') && hasNext) {
|
|
126
|
+
params.title = argv[++i];
|
|
127
|
+
} else if ((a === '-processId' || a === '-ProcessId') && hasNext) {
|
|
128
|
+
params.processId = parseInt(argv[++i], 10);
|
|
129
|
+
}
|
|
130
|
+
// -Restore has no direct macOS equivalent; silently ignore
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// Validate parameters
|
|
134
|
+
if (!params.title && !params.processId) {
|
|
135
|
+
return JSON.stringify({
|
|
136
|
+
success: false,
|
|
137
|
+
error: 'Must specify -title or -processId'
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
var SystemEvents = Application('System Events');
|
|
142
|
+
SystemEvents.includeStandardAdditions = true;
|
|
143
|
+
|
|
144
|
+
// Get all processes with UI
|
|
145
|
+
var processes = SystemEvents.processes.where({ backgroundOnly: false });
|
|
146
|
+
var targetWindow = null;
|
|
147
|
+
var targetProcess = null;
|
|
148
|
+
|
|
149
|
+
// Phase 1: if processId is supplied, locate that process and pick
|
|
150
|
+
// the right window inside it. This is the bug #2 fix — when title
|
|
151
|
+
// is also supplied, the title MUST be consulted before defaulting
|
|
152
|
+
// to windows[0] (which on multi-window apps is often a popup).
|
|
153
|
+
if (params.processId) {
|
|
154
|
+
for (var pi = 0; pi < processes.length; pi++) {
|
|
155
|
+
try {
|
|
156
|
+
var process = processes[pi];
|
|
157
|
+
var pidHere = safeGet(process, 'unixId', 0);
|
|
158
|
+
if (pidHere !== params.processId) continue;
|
|
159
|
+
|
|
160
|
+
targetProcess = process;
|
|
161
|
+
var windows = process.windows;
|
|
162
|
+
|
|
163
|
+
if (windows.length === 0) break;
|
|
164
|
+
|
|
165
|
+
// (a) Title-disambiguation inside the matched process
|
|
166
|
+
if (params.title) {
|
|
167
|
+
var hit = findTitleMatchWindow(windows, params.title);
|
|
168
|
+
if (hit) { targetWindow = hit.window; break; }
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// (b) No title or no title match — fall back to the
|
|
172
|
+
// scored interaction window rather than blindly windows[0],
|
|
173
|
+
// which would re-introduce the popup-grab regression.
|
|
174
|
+
var picked = pickInteractionWindow(windows);
|
|
175
|
+
if (picked) { targetWindow = picked.window; break; }
|
|
176
|
+
|
|
177
|
+
// (c) Last resort: keep legacy behaviour so we never
|
|
178
|
+
// regress to "matched process but no window".
|
|
179
|
+
targetWindow = windows[0];
|
|
180
|
+
break;
|
|
181
|
+
} catch (e) {
|
|
182
|
+
// Process not accessible
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// Phase 2: cross-process title scan, used when processId wasn't
|
|
188
|
+
// supplied or didn't match. Bug #2's second clause: this scan must
|
|
189
|
+
// skip 0x0 (invisible) windows that happen to share the title but
|
|
190
|
+
// can't actually be raised — those return success but do nothing.
|
|
191
|
+
if (!targetWindow && params.title) {
|
|
192
|
+
for (var pi2 = 0; pi2 < processes.length; pi2++) {
|
|
193
|
+
try {
|
|
194
|
+
var p2 = processes[pi2];
|
|
195
|
+
var ws = p2.windows;
|
|
196
|
+
var hit2 = findTitleMatchWindow(ws, params.title);
|
|
197
|
+
if (hit2) {
|
|
198
|
+
targetWindow = hit2.window;
|
|
199
|
+
targetProcess = p2;
|
|
200
|
+
break;
|
|
201
|
+
}
|
|
202
|
+
} catch (e) {
|
|
203
|
+
// Process not accessible
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
if (!targetWindow) {
|
|
209
|
+
return JSON.stringify({
|
|
210
|
+
success: false,
|
|
211
|
+
error: 'Window not found matching title="' + (params.title || '') + '" processId=' + (params.processId || 0)
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// Bring window to front
|
|
216
|
+
// Method 1: Use System Events to set frontmost
|
|
217
|
+
try {
|
|
218
|
+
if (targetProcess) {
|
|
219
|
+
targetProcess.frontmost = true;
|
|
220
|
+
}
|
|
221
|
+
} catch (e) {
|
|
222
|
+
// Continue with other methods
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
// Method 2: Focus the window
|
|
226
|
+
try {
|
|
227
|
+
targetWindow.focused = true;
|
|
228
|
+
} catch (e) {
|
|
229
|
+
// Continue
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
// Method 3: Use application level activation (if we can get the app name)
|
|
233
|
+
try {
|
|
234
|
+
if (targetProcess) {
|
|
235
|
+
var appName = safeGet(targetProcess, 'name', '');
|
|
236
|
+
if (appName) {
|
|
237
|
+
var app = Application(appName);
|
|
238
|
+
app.activate();
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
} catch (e) {
|
|
242
|
+
// Continue
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
// Method 4: AXRaise — the only call that, on multi-window apps, brings
|
|
246
|
+
// the SPECIFIC window forward instead of just activating the app and
|
|
247
|
+
// letting macOS surface whichever window was frontmost last. Without
|
|
248
|
+
// this, bug #2 would only be half-fixed: we'd pick the right window
|
|
249
|
+
// but app.activate() would still raise the app's most-recent window.
|
|
250
|
+
try {
|
|
251
|
+
targetWindow.actions.byName('AXRaise').perform();
|
|
252
|
+
} catch (e) {
|
|
253
|
+
// Not all windows expose AXRaise; ignore if unavailable.
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
// Get final window info
|
|
257
|
+
var winTitle = '';
|
|
258
|
+
var winProcessId = 0;
|
|
259
|
+
var winProcessName = 'unknown';
|
|
260
|
+
var winBounds = getWinBounds(targetWindow);
|
|
261
|
+
var winSubrole = getWinSubrole(targetWindow);
|
|
262
|
+
|
|
263
|
+
try {
|
|
264
|
+
winTitle = targetWindow.name() || '';
|
|
265
|
+
} catch (e) {}
|
|
266
|
+
|
|
267
|
+
if (targetProcess) {
|
|
268
|
+
winProcessId = safeGet(targetProcess, 'unixId', 0);
|
|
269
|
+
winProcessName = safeGet(targetProcess, 'name', 'unknown');
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
return JSON.stringify({
|
|
273
|
+
success: true,
|
|
274
|
+
title: winTitle,
|
|
275
|
+
processId: winProcessId,
|
|
276
|
+
processName: winProcessName,
|
|
277
|
+
bounds: winBounds,
|
|
278
|
+
subrole: winSubrole
|
|
279
|
+
});
|
|
280
|
+
|
|
281
|
+
} catch (error) {
|
|
282
|
+
return JSON.stringify({ success: false, error: error.toString() });
|
|
283
|
+
}
|
|
284
|
+
}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
#!/usr/bin/env osascript -l JavaScript
|
|
2
|
+
/**
|
|
3
|
+
* get-focused-element.jxa — returns the currently focused UI element on macOS.
|
|
4
|
+
*
|
|
5
|
+
* Output: JSON with name, role, value, bounds, processId.
|
|
6
|
+
* Matches the FocusedElementInfo shape from accessibility.ts.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
function run() {
|
|
10
|
+
const se = Application('System Events');
|
|
11
|
+
se.includeStandardAdditions = true;
|
|
12
|
+
|
|
13
|
+
try {
|
|
14
|
+
const procs = se.applicationProcesses.whose({ frontmost: true });
|
|
15
|
+
if (procs.length === 0) {
|
|
16
|
+
return JSON.stringify(null);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const proc = procs[0];
|
|
20
|
+
const pid = proc.unixId();
|
|
21
|
+
const procName = proc.name();
|
|
22
|
+
|
|
23
|
+
// Get the focused UI element
|
|
24
|
+
let focused;
|
|
25
|
+
try {
|
|
26
|
+
focused = proc.focusedUIElement();
|
|
27
|
+
} catch (e) {
|
|
28
|
+
// Some apps don't expose focused element — return window info instead
|
|
29
|
+
try {
|
|
30
|
+
const wins = proc.windows();
|
|
31
|
+
if (wins.length > 0) {
|
|
32
|
+
const win = wins[0];
|
|
33
|
+
const pos = win.position();
|
|
34
|
+
const sz = win.size();
|
|
35
|
+
return JSON.stringify({
|
|
36
|
+
name: win.name() || procName,
|
|
37
|
+
automationId: '',
|
|
38
|
+
controlType: 'Window',
|
|
39
|
+
className: procName,
|
|
40
|
+
processId: pid,
|
|
41
|
+
isEnabled: true,
|
|
42
|
+
bounds: { x: pos[0], y: pos[1], width: sz[0], height: sz[1] },
|
|
43
|
+
value: ''
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
} catch (_) {}
|
|
47
|
+
return JSON.stringify(null);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Extract properties safely
|
|
51
|
+
let name = '', role = '', value = '';
|
|
52
|
+
let bounds = { x: 0, y: 0, width: 0, height: 0 };
|
|
53
|
+
|
|
54
|
+
try { name = focused.name() || ''; } catch (_) {}
|
|
55
|
+
try { role = focused.role() || ''; } catch (_) {}
|
|
56
|
+
try { value = String(focused.value() || ''); } catch (_) {}
|
|
57
|
+
|
|
58
|
+
try {
|
|
59
|
+
const pos = focused.position();
|
|
60
|
+
const sz = focused.size();
|
|
61
|
+
bounds = { x: pos[0], y: pos[1], width: sz[0], height: sz[1] };
|
|
62
|
+
} catch (_) {}
|
|
63
|
+
|
|
64
|
+
// Map AX roles to Windows ControlType names for compatibility
|
|
65
|
+
const roleMap = {
|
|
66
|
+
'AXTextField': 'Edit',
|
|
67
|
+
'AXTextArea': 'Edit',
|
|
68
|
+
'AXButton': 'Button',
|
|
69
|
+
'AXCheckBox': 'CheckBox',
|
|
70
|
+
'AXRadioButton': 'RadioButton',
|
|
71
|
+
'AXComboBox': 'ComboBox',
|
|
72
|
+
'AXList': 'List',
|
|
73
|
+
'AXTable': 'Table',
|
|
74
|
+
'AXMenu': 'Menu',
|
|
75
|
+
'AXMenuItem': 'MenuItem',
|
|
76
|
+
'AXStaticText': 'Text',
|
|
77
|
+
'AXImage': 'Image',
|
|
78
|
+
'AXLink': 'Hyperlink',
|
|
79
|
+
'AXGroup': 'Group',
|
|
80
|
+
'AXWindow': 'Window',
|
|
81
|
+
'AXWebArea': 'Document',
|
|
82
|
+
'AXScrollArea': 'ScrollBar',
|
|
83
|
+
'AXTab': 'Tab',
|
|
84
|
+
'AXSlider': 'Slider',
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
const controlType = roleMap[role] || role.replace('AX', '') || 'Unknown';
|
|
88
|
+
|
|
89
|
+
return JSON.stringify({
|
|
90
|
+
name: name,
|
|
91
|
+
automationId: '',
|
|
92
|
+
controlType: controlType,
|
|
93
|
+
className: procName,
|
|
94
|
+
processId: pid,
|
|
95
|
+
isEnabled: true,
|
|
96
|
+
bounds: bounds,
|
|
97
|
+
value: value
|
|
98
|
+
});
|
|
99
|
+
} catch (e) {
|
|
100
|
+
return JSON.stringify(null);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
#!/usr/bin/env osascript -l JavaScript
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* get-foreground-window.jxa
|
|
5
|
+
*
|
|
6
|
+
* Gets the currently focused/foreground window.
|
|
7
|
+
*
|
|
8
|
+
* Returns JSON with:
|
|
9
|
+
* - handle: Window identifier (on macOS this is the process ID as a proxy)
|
|
10
|
+
* - processId: Unix process ID of the foreground application
|
|
11
|
+
* - processName: Name of the foreground application
|
|
12
|
+
* - title: Title of the chosen window
|
|
13
|
+
* - bounds: {x, y, width, height} of the chosen window
|
|
14
|
+
* - subrole: AXSubrole of the chosen window ("AXStandardWindow", etc.)
|
|
15
|
+
* - success: Boolean indicating success
|
|
16
|
+
*
|
|
17
|
+
* Bug #4 of clawdcursor issue #101: the previous implementation took
|
|
18
|
+
* processes.where({frontmost: true})[0].windows[0], which on apps that
|
|
19
|
+
* spawn a popup (Xcode "Downloads", Safari "Notifications") returned the
|
|
20
|
+
* popup instead of the user's actual interaction window. We now score
|
|
21
|
+
* each window of the frontmost process and pick the best one.
|
|
22
|
+
*
|
|
23
|
+
* Usage: osascript -l JavaScript get-foreground-window.jxa
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
// ── Inlined from scripts/mac/_window-picker.jxa ───────────────────
|
|
27
|
+
// Keep these helpers in sync with _window-picker.jxa; osascript can't require().
|
|
28
|
+
|
|
29
|
+
function safeGet(obj, property, defaultValue) {
|
|
30
|
+
try {
|
|
31
|
+
var val = obj[property]();
|
|
32
|
+
return (val !== undefined && val !== null) ? val : defaultValue;
|
|
33
|
+
} catch (e) {
|
|
34
|
+
return defaultValue;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function getWinBounds(win) {
|
|
39
|
+
var bounds = { x: 0, y: 0, width: 0, height: 0 };
|
|
40
|
+
try {
|
|
41
|
+
var p = win.position();
|
|
42
|
+
var s = win.size();
|
|
43
|
+
if (p && s) {
|
|
44
|
+
bounds.x = p[0] || 0;
|
|
45
|
+
bounds.y = p[1] || 0;
|
|
46
|
+
bounds.width = s[0] || 0;
|
|
47
|
+
bounds.height = s[1] || 0;
|
|
48
|
+
}
|
|
49
|
+
} catch (e) {}
|
|
50
|
+
return bounds;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function getWinSubrole(win) {
|
|
54
|
+
try { return win.subrole() || ''; } catch (e) {}
|
|
55
|
+
try { return win.attributes.byName('AXSubrole').value() || ''; } catch (e) {}
|
|
56
|
+
return '';
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function isLikelyTrayTitle(title) {
|
|
60
|
+
if (!title) return false;
|
|
61
|
+
var t = String(title).trim().toLowerCase();
|
|
62
|
+
return t === 'downloads' || t === 'notifications' || t === 'updates' ||
|
|
63
|
+
t === 'inspector' || t === 'activity';
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function scoreWindow(win, index) {
|
|
67
|
+
var bounds = getWinBounds(win);
|
|
68
|
+
var area = bounds.width * bounds.height;
|
|
69
|
+
if (area <= 0) return -Infinity;
|
|
70
|
+
var score = area;
|
|
71
|
+
var subrole = getWinSubrole(win);
|
|
72
|
+
if (subrole === 'AXStandardWindow') score += 2000000;
|
|
73
|
+
else if (subrole === 'AXFloatingWindow') score -= 5000000;
|
|
74
|
+
else if (subrole === 'AXDialog' || subrole === 'AXSystemDialog') score -= 3000000;
|
|
75
|
+
var title = '';
|
|
76
|
+
try { title = win.name() || ''; } catch (e) {}
|
|
77
|
+
if (isLikelyTrayTitle(title)) score -= 1500000;
|
|
78
|
+
score += index * 10;
|
|
79
|
+
return score;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function pickInteractionWindow(windows) {
|
|
83
|
+
var best = null, bestScore = -Infinity, bestIndex = -1;
|
|
84
|
+
for (var i = 0; i < windows.length; i++) {
|
|
85
|
+
try {
|
|
86
|
+
var s = scoreWindow(windows[i], i);
|
|
87
|
+
if (s > bestScore) { bestScore = s; best = windows[i]; bestIndex = i; }
|
|
88
|
+
} catch (e) {}
|
|
89
|
+
}
|
|
90
|
+
if (!best) return null;
|
|
91
|
+
return { window: best, index: bestIndex };
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// ── End inlined helpers ───────────────────────────────────────────
|
|
95
|
+
|
|
96
|
+
// run(argv) is the JXA entry point — osascript calls it and its return value becomes stdout.
|
|
97
|
+
function run(argv) {
|
|
98
|
+
try {
|
|
99
|
+
var SystemEvents = Application('System Events');
|
|
100
|
+
SystemEvents.includeStandardAdditions = true;
|
|
101
|
+
|
|
102
|
+
// Get the frontmost process
|
|
103
|
+
var frontProcesses = SystemEvents.processes.where({ frontmost: true });
|
|
104
|
+
|
|
105
|
+
if (frontProcesses.length === 0) {
|
|
106
|
+
return JSON.stringify({
|
|
107
|
+
success: false,
|
|
108
|
+
error: 'No foreground window found'
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
var frontProcess = frontProcesses[0];
|
|
113
|
+
var processId = safeGet(frontProcess, 'unixId', 0);
|
|
114
|
+
var processName = safeGet(frontProcess, 'name', 'unknown');
|
|
115
|
+
|
|
116
|
+
// Pick the user's real interaction window instead of windows[0].
|
|
117
|
+
// See header comment for bug #4 rationale.
|
|
118
|
+
var title = '';
|
|
119
|
+
var bounds = { x: 0, y: 0, width: 0, height: 0 };
|
|
120
|
+
var subrole = '';
|
|
121
|
+
|
|
122
|
+
try {
|
|
123
|
+
var windows = frontProcess.windows;
|
|
124
|
+
if (windows.length > 0) {
|
|
125
|
+
var picked = pickInteractionWindow(windows);
|
|
126
|
+
var chosen = null;
|
|
127
|
+
|
|
128
|
+
if (picked) {
|
|
129
|
+
chosen = picked.window;
|
|
130
|
+
} else {
|
|
131
|
+
// No window had visible area — try a focused window as a
|
|
132
|
+
// last resort before giving up on title/bounds entirely.
|
|
133
|
+
for (var i = 0; i < windows.length; i++) {
|
|
134
|
+
try {
|
|
135
|
+
if (windows[i].focused && windows[i].focused()) {
|
|
136
|
+
chosen = windows[i];
|
|
137
|
+
break;
|
|
138
|
+
}
|
|
139
|
+
} catch (e) {}
|
|
140
|
+
}
|
|
141
|
+
// If still nothing, fall back to windows[0] purely for
|
|
142
|
+
// title extraction — preserves prior behaviour when the
|
|
143
|
+
// only available window is a popup.
|
|
144
|
+
if (!chosen) chosen = windows[0];
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
try { title = chosen.name() || ''; } catch (e) {}
|
|
148
|
+
bounds = getWinBounds(chosen);
|
|
149
|
+
subrole = getWinSubrole(chosen);
|
|
150
|
+
}
|
|
151
|
+
} catch (e) {
|
|
152
|
+
// No windows accessible
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// On macOS, we don't have window handles like Win32 HWND
|
|
156
|
+
// We use processId as a proxy identifier
|
|
157
|
+
return JSON.stringify({
|
|
158
|
+
success: true,
|
|
159
|
+
handle: processId, // Using processId as handle equivalent
|
|
160
|
+
processId: processId,
|
|
161
|
+
processName: processName,
|
|
162
|
+
title: title,
|
|
163
|
+
bounds: bounds,
|
|
164
|
+
subrole: subrole
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
} catch (error) {
|
|
168
|
+
return JSON.stringify({
|
|
169
|
+
success: false,
|
|
170
|
+
error: error.toString()
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
}
|