gclm-code 1.0.0 → 1.0.1
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/README.md +1 -1
- package/bin/gc.js +53 -25
- package/bin/install-runtime.js +253 -0
- package/package.json +10 -5
- package/vendor/manifest.json +92 -0
- package/vendor/modules/node_modules/@ant/claude-for-chrome-mcp/package.json +9 -0
- package/vendor/modules/node_modules/@ant/claude-for-chrome-mcp/src/bridgeClient.ts +1126 -0
- package/vendor/modules/node_modules/@ant/claude-for-chrome-mcp/src/browserTools.ts +546 -0
- package/vendor/modules/node_modules/@ant/claude-for-chrome-mcp/src/index.ts +15 -0
- package/vendor/modules/node_modules/@ant/claude-for-chrome-mcp/src/mcpServer.ts +96 -0
- package/vendor/modules/node_modules/@ant/claude-for-chrome-mcp/src/mcpSocketClient.ts +493 -0
- package/vendor/modules/node_modules/@ant/claude-for-chrome-mcp/src/mcpSocketPool.ts +327 -0
- package/vendor/modules/node_modules/@ant/claude-for-chrome-mcp/src/toolCalls.ts +301 -0
- package/vendor/modules/node_modules/@ant/claude-for-chrome-mcp/src/types.ts +134 -0
- package/vendor/modules/node_modules/@ant/computer-use-input/package.json +9 -0
- package/vendor/modules/node_modules/@ant/computer-use-input/src/driver-jxa.js +341 -0
- package/vendor/modules/node_modules/@ant/computer-use-input/src/driver-swift.swift +417 -0
- package/vendor/modules/node_modules/@ant/computer-use-input/src/implementation.js +204 -0
- package/vendor/modules/node_modules/@ant/computer-use-input/src/index.js +5 -0
- package/vendor/modules/node_modules/@ant/computer-use-mcp/package.json +11 -0
- package/vendor/modules/node_modules/@ant/computer-use-mcp/src/deniedApps.ts +553 -0
- package/vendor/modules/node_modules/@ant/computer-use-mcp/src/imageResize.ts +108 -0
- package/vendor/modules/node_modules/@ant/computer-use-mcp/src/index.ts +69 -0
- package/vendor/modules/node_modules/@ant/computer-use-mcp/src/keyBlocklist.ts +153 -0
- package/vendor/modules/node_modules/@ant/computer-use-mcp/src/mcpServer.ts +313 -0
- package/vendor/modules/node_modules/@ant/computer-use-mcp/src/pixelCompare.ts +171 -0
- package/vendor/modules/node_modules/@ant/computer-use-mcp/src/sentinelApps.ts +43 -0
- package/vendor/modules/node_modules/@ant/computer-use-mcp/src/subGates.ts +19 -0
- package/vendor/modules/node_modules/@ant/computer-use-mcp/src/toolCalls.ts +3872 -0
- package/vendor/modules/node_modules/@ant/computer-use-mcp/src/tools.ts +706 -0
- package/vendor/modules/node_modules/@ant/computer-use-mcp/src/types.ts +635 -0
- package/vendor/modules/node_modules/@ant/computer-use-swift/package.json +9 -0
- package/vendor/modules/node_modules/@ant/computer-use-swift/src/driver-jxa.js +108 -0
- package/vendor/modules/node_modules/@ant/computer-use-swift/src/implementation.js +706 -0
- package/vendor/modules/node_modules/@ant/computer-use-swift/src/index.js +7 -0
- package/vendor/modules/node_modules/audio-capture-napi/package.json +8 -0
- package/vendor/modules/node_modules/audio-capture-napi/src/index.ts +226 -0
- package/vendor/modules/node_modules/image-processor-napi/package.json +11 -0
- package/vendor/modules/node_modules/image-processor-napi/src/index.ts +396 -0
- package/vendor/modules/node_modules/modifiers-napi/package.json +8 -0
- package/vendor/modules/node_modules/modifiers-napi/src/index.ts +79 -0
- package/vendor/modules/node_modules/url-handler-napi/package.json +8 -0
- package/vendor/modules/node_modules/url-handler-napi/src/index.ts +62 -0
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
export interface Logger {
|
|
2
|
+
info: (message: string, ...args: unknown[]) => void;
|
|
3
|
+
error: (message: string, ...args: unknown[]) => void;
|
|
4
|
+
warn: (message: string, ...args: unknown[]) => void;
|
|
5
|
+
debug: (message: string, ...args: unknown[]) => void;
|
|
6
|
+
silly: (message: string, ...args: unknown[]) => void;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export type PermissionMode =
|
|
10
|
+
| "ask"
|
|
11
|
+
| "skip_all_permission_checks"
|
|
12
|
+
| "follow_a_plan";
|
|
13
|
+
|
|
14
|
+
export interface BridgeConfig {
|
|
15
|
+
/** Bridge WebSocket base URL (e.g., wss://bridge.claudeusercontent.com) */
|
|
16
|
+
url: string;
|
|
17
|
+
/** Returns the user's account UUID for the connection path */
|
|
18
|
+
getUserId: () => Promise<string | undefined>;
|
|
19
|
+
/** Returns a valid OAuth token for bridge authentication */
|
|
20
|
+
getOAuthToken: () => Promise<string | undefined>;
|
|
21
|
+
/** Optional dev user ID for local development (bypasses OAuth) */
|
|
22
|
+
devUserId?: string;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/** Metadata about a connected Chrome extension instance. */
|
|
26
|
+
export interface ChromeExtensionInfo {
|
|
27
|
+
deviceId: string;
|
|
28
|
+
osPlatform?: string;
|
|
29
|
+
connectedAt: number;
|
|
30
|
+
name?: string;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export interface ClaudeForChromeContext {
|
|
34
|
+
serverName: string;
|
|
35
|
+
logger: Logger;
|
|
36
|
+
socketPath: string;
|
|
37
|
+
// Optional dynamic resolver for socket path. When provided, called on each
|
|
38
|
+
// connection attempt to handle runtime conditions (e.g., TMPDIR mismatch).
|
|
39
|
+
getSocketPath?: () => string;
|
|
40
|
+
// Optional resolver returning all available socket paths (for multi-profile support).
|
|
41
|
+
// When provided, a socket pool connects to all sockets and routes by tab ID.
|
|
42
|
+
getSocketPaths?: () => string[];
|
|
43
|
+
clientTypeId: string; // "desktop" | "claude-code"
|
|
44
|
+
onToolCallDisconnected: () => string;
|
|
45
|
+
onAuthenticationError: () => void;
|
|
46
|
+
isDisabled?: () => boolean;
|
|
47
|
+
/** Bridge WebSocket configuration. When provided, uses bridge instead of socket. */
|
|
48
|
+
bridgeConfig?: BridgeConfig;
|
|
49
|
+
/** If set, permission mode is sent to the extension immediately on bridge connection. */
|
|
50
|
+
initialPermissionMode?: PermissionMode;
|
|
51
|
+
/** Optional callback to track telemetry events for bridge connections */
|
|
52
|
+
trackEvent?: <K extends string>(
|
|
53
|
+
eventName: K,
|
|
54
|
+
metadata: Record<string, unknown> | null,
|
|
55
|
+
) => void;
|
|
56
|
+
/** Called when user pairs with an extension via the browser pairing flow. */
|
|
57
|
+
onExtensionPaired?: (deviceId: string, name: string) => void;
|
|
58
|
+
/** Returns the previously paired deviceId, if any. */
|
|
59
|
+
getPersistedDeviceId?: () => string | undefined;
|
|
60
|
+
/** Called when a remote extension is auto-selected (only option available). */
|
|
61
|
+
onRemoteExtensionWarning?: (ext: ChromeExtensionInfo) => void;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Map Node's process.platform to the platform string reported by Chrome extensions
|
|
66
|
+
* via navigator.userAgentData.platform.
|
|
67
|
+
*/
|
|
68
|
+
export function localPlatformLabel(): string {
|
|
69
|
+
return process.platform === "darwin"
|
|
70
|
+
? "macOS"
|
|
71
|
+
: process.platform === "win32"
|
|
72
|
+
? "Windows"
|
|
73
|
+
: "Linux";
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/** Permission request forwarded from the extension to the desktop for user approval. */
|
|
77
|
+
export interface BridgePermissionRequest {
|
|
78
|
+
/** Links to the pending tool_call */
|
|
79
|
+
toolUseId: string;
|
|
80
|
+
/** Unique ID for this permission request */
|
|
81
|
+
requestId: string;
|
|
82
|
+
/** Tool type, e.g. "navigate", "click", "execute_javascript" */
|
|
83
|
+
toolType: string;
|
|
84
|
+
/** The URL/domain context */
|
|
85
|
+
url: string;
|
|
86
|
+
/** Additional action data (click coordinates, text, etc.) */
|
|
87
|
+
actionData?: Record<string, unknown>;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/** Desktop response to a bridge permission request. */
|
|
91
|
+
export interface BridgePermissionResponse {
|
|
92
|
+
requestId: string;
|
|
93
|
+
allowed: boolean;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/** Per-call permission overrides, allowing each session to use its own permission state. */
|
|
97
|
+
export interface PermissionOverrides {
|
|
98
|
+
permissionMode: PermissionMode;
|
|
99
|
+
allowedDomains?: string[];
|
|
100
|
+
/** Callback invoked when the extension requests user permission via the bridge. */
|
|
101
|
+
onPermissionRequest?: (request: BridgePermissionRequest) => Promise<boolean>;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/** Shared interface for McpSocketClient and McpSocketPool */
|
|
105
|
+
export interface SocketClient {
|
|
106
|
+
ensureConnected(): Promise<boolean>;
|
|
107
|
+
callTool(
|
|
108
|
+
name: string,
|
|
109
|
+
args: Record<string, unknown>,
|
|
110
|
+
permissionOverrides?: PermissionOverrides,
|
|
111
|
+
): Promise<unknown>;
|
|
112
|
+
isConnected(): boolean;
|
|
113
|
+
disconnect(): void;
|
|
114
|
+
setNotificationHandler(
|
|
115
|
+
handler: (notification: {
|
|
116
|
+
method: string;
|
|
117
|
+
params?: Record<string, unknown>;
|
|
118
|
+
}) => void,
|
|
119
|
+
): void;
|
|
120
|
+
/** Set permission mode for the current session. Only effective on BridgeClient. */
|
|
121
|
+
setPermissionMode?(
|
|
122
|
+
mode: PermissionMode,
|
|
123
|
+
allowedDomains?: string[],
|
|
124
|
+
): Promise<void>;
|
|
125
|
+
/** Switch to a different browser. Only available on BridgeClient. */
|
|
126
|
+
switchBrowser?(): Promise<
|
|
127
|
+
| {
|
|
128
|
+
deviceId: string;
|
|
129
|
+
name: string;
|
|
130
|
+
}
|
|
131
|
+
| "no_other_browsers"
|
|
132
|
+
| null
|
|
133
|
+
>;
|
|
134
|
+
}
|
|
@@ -0,0 +1,341 @@
|
|
|
1
|
+
ObjC.import('AppKit')
|
|
2
|
+
ObjC.import('ApplicationServices')
|
|
3
|
+
ObjC.import('stdlib')
|
|
4
|
+
|
|
5
|
+
function main() {
|
|
6
|
+
const argv = $.NSProcessInfo.processInfo.arguments
|
|
7
|
+
if (argv.count < 5) {
|
|
8
|
+
throw new Error('Expected JSON payload argument')
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const payload = JSON.parse(ObjC.unwrap(argv.objectAtIndex(argv.count - 1)))
|
|
12
|
+
const result = dispatch(payload)
|
|
13
|
+
if (result !== undefined) {
|
|
14
|
+
$.NSFileHandle.fileHandleWithStandardOutput.writeData(
|
|
15
|
+
$(JSON.stringify(result)).dataUsingEncoding($.NSUTF8StringEncoding),
|
|
16
|
+
)
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function dispatch(payload) {
|
|
21
|
+
switch (payload.op) {
|
|
22
|
+
case 'moveMouse':
|
|
23
|
+
return moveMouse(payload.x, payload.y, payload.dragButton)
|
|
24
|
+
case 'mouseButton':
|
|
25
|
+
return mouseButton(payload.button, payload.action, payload.count)
|
|
26
|
+
case 'mouseLocation':
|
|
27
|
+
return mouseLocation()
|
|
28
|
+
case 'mouseScroll':
|
|
29
|
+
return mouseScroll(payload.amount, payload.axis)
|
|
30
|
+
case 'typeText':
|
|
31
|
+
return typeText(payload.text)
|
|
32
|
+
case 'key':
|
|
33
|
+
return key(payload.key, payload.action)
|
|
34
|
+
case 'keys':
|
|
35
|
+
return keys(payload.keys)
|
|
36
|
+
case 'frontmostAppInfo':
|
|
37
|
+
return frontmostAppInfo()
|
|
38
|
+
default:
|
|
39
|
+
throw new Error(`Unsupported operation: ${payload.op}`)
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const BUTTON_CODES = {
|
|
44
|
+
left: 0,
|
|
45
|
+
right: 1,
|
|
46
|
+
middle: 2,
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const BUTTON_EVENT_TYPES = {
|
|
50
|
+
left: {
|
|
51
|
+
down: $.kCGEventLeftMouseDown,
|
|
52
|
+
up: $.kCGEventLeftMouseUp,
|
|
53
|
+
dragged: $.kCGEventLeftMouseDragged,
|
|
54
|
+
},
|
|
55
|
+
right: {
|
|
56
|
+
down: $.kCGEventRightMouseDown,
|
|
57
|
+
up: $.kCGEventRightMouseUp,
|
|
58
|
+
dragged: $.kCGEventRightMouseDragged,
|
|
59
|
+
},
|
|
60
|
+
middle: {
|
|
61
|
+
down: $.kCGEventOtherMouseDown,
|
|
62
|
+
up: $.kCGEventOtherMouseUp,
|
|
63
|
+
dragged: $.kCGEventOtherMouseDragged,
|
|
64
|
+
},
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const KEY_CODES = {
|
|
68
|
+
a: 0,
|
|
69
|
+
s: 1,
|
|
70
|
+
d: 2,
|
|
71
|
+
f: 3,
|
|
72
|
+
h: 4,
|
|
73
|
+
g: 5,
|
|
74
|
+
z: 6,
|
|
75
|
+
x: 7,
|
|
76
|
+
c: 8,
|
|
77
|
+
v: 9,
|
|
78
|
+
b: 11,
|
|
79
|
+
q: 12,
|
|
80
|
+
w: 13,
|
|
81
|
+
e: 14,
|
|
82
|
+
r: 15,
|
|
83
|
+
y: 16,
|
|
84
|
+
t: 17,
|
|
85
|
+
'1': 18,
|
|
86
|
+
'2': 19,
|
|
87
|
+
'3': 20,
|
|
88
|
+
'4': 21,
|
|
89
|
+
'6': 22,
|
|
90
|
+
'5': 23,
|
|
91
|
+
'=': 24,
|
|
92
|
+
'9': 25,
|
|
93
|
+
'7': 26,
|
|
94
|
+
'-': 27,
|
|
95
|
+
'8': 28,
|
|
96
|
+
'0': 29,
|
|
97
|
+
']': 30,
|
|
98
|
+
o: 31,
|
|
99
|
+
u: 32,
|
|
100
|
+
'[': 33,
|
|
101
|
+
i: 34,
|
|
102
|
+
p: 35,
|
|
103
|
+
l: 37,
|
|
104
|
+
j: 38,
|
|
105
|
+
"'": 39,
|
|
106
|
+
k: 40,
|
|
107
|
+
';': 41,
|
|
108
|
+
'\\': 42,
|
|
109
|
+
',': 43,
|
|
110
|
+
'/': 44,
|
|
111
|
+
n: 45,
|
|
112
|
+
m: 46,
|
|
113
|
+
'.': 47,
|
|
114
|
+
'`': 50,
|
|
115
|
+
return: 36,
|
|
116
|
+
enter: 76,
|
|
117
|
+
tab: 48,
|
|
118
|
+
space: 49,
|
|
119
|
+
delete: 51,
|
|
120
|
+
escape: 53,
|
|
121
|
+
command: 55,
|
|
122
|
+
shift: 56,
|
|
123
|
+
capslock: 57,
|
|
124
|
+
option: 58,
|
|
125
|
+
control: 59,
|
|
126
|
+
rightshift: 60,
|
|
127
|
+
rightoption: 61,
|
|
128
|
+
rightcontrol: 62,
|
|
129
|
+
function: 63,
|
|
130
|
+
f17: 64,
|
|
131
|
+
volumeup: 72,
|
|
132
|
+
volumedown: 73,
|
|
133
|
+
mute: 74,
|
|
134
|
+
f18: 79,
|
|
135
|
+
f19: 80,
|
|
136
|
+
f20: 90,
|
|
137
|
+
f5: 96,
|
|
138
|
+
f6: 97,
|
|
139
|
+
f7: 98,
|
|
140
|
+
f3: 99,
|
|
141
|
+
f8: 100,
|
|
142
|
+
f9: 101,
|
|
143
|
+
f11: 103,
|
|
144
|
+
f13: 105,
|
|
145
|
+
f16: 106,
|
|
146
|
+
f14: 107,
|
|
147
|
+
f10: 109,
|
|
148
|
+
f12: 111,
|
|
149
|
+
f15: 113,
|
|
150
|
+
help: 114,
|
|
151
|
+
home: 115,
|
|
152
|
+
pageup: 116,
|
|
153
|
+
forwarddelete: 117,
|
|
154
|
+
f4: 118,
|
|
155
|
+
end: 119,
|
|
156
|
+
f2: 120,
|
|
157
|
+
pagedown: 121,
|
|
158
|
+
f1: 122,
|
|
159
|
+
left: 123,
|
|
160
|
+
right: 124,
|
|
161
|
+
down: 125,
|
|
162
|
+
up: 126,
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
const MODIFIER_FLAGS = {
|
|
166
|
+
command: $.kCGEventFlagMaskCommand,
|
|
167
|
+
shift: $.kCGEventFlagMaskShift,
|
|
168
|
+
option: $.kCGEventFlagMaskAlternate,
|
|
169
|
+
control: $.kCGEventFlagMaskControl,
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
function point(x, y) {
|
|
173
|
+
return $.CGPointMake(x, y)
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
function postMouseEvent(type, location, button, clickState) {
|
|
177
|
+
const event = $.CGEventCreateMouseEvent(
|
|
178
|
+
null,
|
|
179
|
+
type,
|
|
180
|
+
location,
|
|
181
|
+
BUTTON_CODES[button] ?? 0,
|
|
182
|
+
)
|
|
183
|
+
if (clickState !== undefined) {
|
|
184
|
+
$.CGEventSetIntegerValueField(event, $.kCGMouseEventClickState, clickState)
|
|
185
|
+
}
|
|
186
|
+
$.CGEventPost($.kCGHIDEventTap, event)
|
|
187
|
+
$.CFRelease(event)
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
function currentMouseLocation() {
|
|
191
|
+
const event = $.CGEventCreate(null)
|
|
192
|
+
const location = $.CGEventGetLocation(event)
|
|
193
|
+
$.CFRelease(event)
|
|
194
|
+
return location
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
function moveMouse(x, y, dragButton) {
|
|
198
|
+
const location = point(x, y)
|
|
199
|
+
if (dragButton) {
|
|
200
|
+
const types = BUTTON_EVENT_TYPES[dragButton]
|
|
201
|
+
postMouseEvent(types?.dragged ?? $.kCGEventLeftMouseDragged, location, dragButton)
|
|
202
|
+
return true
|
|
203
|
+
}
|
|
204
|
+
postMouseEvent($.kCGEventMouseMoved, location, 'left')
|
|
205
|
+
return true
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
function mouseButton(button, action, count) {
|
|
209
|
+
const location = currentMouseLocation()
|
|
210
|
+
const types = BUTTON_EVENT_TYPES[button]
|
|
211
|
+
if (!types) {
|
|
212
|
+
throw new Error(`Unsupported mouse button: ${button}`)
|
|
213
|
+
}
|
|
214
|
+
const normalizedAction =
|
|
215
|
+
action === 'press' ? 'down' : action === 'release' ? 'up' : action
|
|
216
|
+
if (normalizedAction === 'click') {
|
|
217
|
+
const clickCount = Math.max(1, Number(count) || 1)
|
|
218
|
+
for (let i = 1; i <= clickCount; i++) {
|
|
219
|
+
postMouseEvent(types.down, location, button, i)
|
|
220
|
+
postMouseEvent(types.up, location, button, i)
|
|
221
|
+
}
|
|
222
|
+
return true
|
|
223
|
+
}
|
|
224
|
+
const type = types[normalizedAction]
|
|
225
|
+
if (type === undefined) {
|
|
226
|
+
throw new Error(`Unsupported mouse action: ${action}`)
|
|
227
|
+
}
|
|
228
|
+
postMouseEvent(type, location, button, count)
|
|
229
|
+
return true
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
function mouseLocation() {
|
|
233
|
+
const location = currentMouseLocation()
|
|
234
|
+
return { x: Math.round(location.x), y: Math.round(location.y) }
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
function mouseScroll(amount, axis) {
|
|
238
|
+
const vertical = axis === 'y' || axis === 'vertical' ? amount : 0
|
|
239
|
+
const horizontal = axis === 'x' || axis === 'horizontal' ? amount : 0
|
|
240
|
+
const event = $.CGEventCreateScrollWheelEvent(
|
|
241
|
+
null,
|
|
242
|
+
$.kCGScrollEventUnitPixel,
|
|
243
|
+
2,
|
|
244
|
+
vertical,
|
|
245
|
+
horizontal,
|
|
246
|
+
)
|
|
247
|
+
$.CGEventPost($.kCGHIDEventTap, event)
|
|
248
|
+
$.CFRelease(event)
|
|
249
|
+
return true
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
function typeText(text) {
|
|
253
|
+
const source = $.CGEventSourceCreate($.kCGEventSourceStateHIDSystemState)
|
|
254
|
+
for (const ch of text) {
|
|
255
|
+
const down = $.CGEventCreateKeyboardEvent(source, 0, true)
|
|
256
|
+
$.CGEventKeyboardSetUnicodeString(down, 1, $(ch))
|
|
257
|
+
$.CGEventPost($.kCGHIDEventTap, down)
|
|
258
|
+
$.CFRelease(down)
|
|
259
|
+
|
|
260
|
+
const up = $.CGEventCreateKeyboardEvent(source, 0, false)
|
|
261
|
+
$.CGEventKeyboardSetUnicodeString(up, 1, $(ch))
|
|
262
|
+
$.CGEventPost($.kCGHIDEventTap, up)
|
|
263
|
+
$.CFRelease(up)
|
|
264
|
+
}
|
|
265
|
+
$.CFRelease(source)
|
|
266
|
+
return true
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
function key(name, action) {
|
|
270
|
+
const keyCode = KEY_CODES[String(name).toLowerCase()]
|
|
271
|
+
if (keyCode === undefined) {
|
|
272
|
+
throw new Error(`Unsupported key: ${name}`)
|
|
273
|
+
}
|
|
274
|
+
const normalizedAction = String(action ?? 'press').toLowerCase()
|
|
275
|
+
if (
|
|
276
|
+
normalizedAction !== 'down' &&
|
|
277
|
+
normalizedAction !== 'up' &&
|
|
278
|
+
normalizedAction !== 'press' &&
|
|
279
|
+
normalizedAction !== 'release'
|
|
280
|
+
) {
|
|
281
|
+
throw new Error(`Unsupported key action: ${action}`)
|
|
282
|
+
}
|
|
283
|
+
const isDown = normalizedAction === 'down' || normalizedAction === 'press'
|
|
284
|
+
const event = $.CGEventCreateKeyboardEvent(null, keyCode, isDown)
|
|
285
|
+
$.CGEventPost($.kCGHIDEventTap, event)
|
|
286
|
+
$.CFRelease(event)
|
|
287
|
+
return true
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
function keys(names) {
|
|
291
|
+
let flags = 0
|
|
292
|
+
for (const name of names) {
|
|
293
|
+
const normalized = String(name).toLowerCase()
|
|
294
|
+
const keyCode = KEY_CODES[normalized]
|
|
295
|
+
if (keyCode === undefined) {
|
|
296
|
+
throw new Error(`Unsupported key: ${name}`)
|
|
297
|
+
}
|
|
298
|
+
const event = $.CGEventCreateKeyboardEvent(null, keyCode, true)
|
|
299
|
+
const modifierFlag = MODIFIER_FLAGS[normalized]
|
|
300
|
+
if (modifierFlag) {
|
|
301
|
+
flags |= modifierFlag
|
|
302
|
+
$.CGEventSetFlags(event, flags)
|
|
303
|
+
} else if (flags) {
|
|
304
|
+
$.CGEventSetFlags(event, flags)
|
|
305
|
+
}
|
|
306
|
+
$.CGEventPost($.kCGHIDEventTap, event)
|
|
307
|
+
$.CFRelease(event)
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
for (const name of [...names].reverse()) {
|
|
311
|
+
const normalized = String(name).toLowerCase()
|
|
312
|
+
const keyCode = KEY_CODES[normalized]
|
|
313
|
+
const event = $.CGEventCreateKeyboardEvent(null, keyCode, false)
|
|
314
|
+
const modifierFlag = MODIFIER_FLAGS[normalized]
|
|
315
|
+
if (modifierFlag) {
|
|
316
|
+
flags &= ~modifierFlag
|
|
317
|
+
$.CGEventSetFlags(event, flags)
|
|
318
|
+
} else if (flags) {
|
|
319
|
+
$.CGEventSetFlags(event, flags)
|
|
320
|
+
}
|
|
321
|
+
$.CGEventPost($.kCGHIDEventTap, event)
|
|
322
|
+
$.CFRelease(event)
|
|
323
|
+
}
|
|
324
|
+
return true
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
function frontmostAppInfo() {
|
|
328
|
+
const workspace = $.NSWorkspace.sharedWorkspace
|
|
329
|
+
const app = workspace.frontmostApplication
|
|
330
|
+
if (!app) {
|
|
331
|
+
return null
|
|
332
|
+
}
|
|
333
|
+
const name = ObjC.unwrap(app.localizedName)
|
|
334
|
+
return {
|
|
335
|
+
bundleId: ObjC.unwrap(app.bundleIdentifier),
|
|
336
|
+
appName: name,
|
|
337
|
+
name,
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
main()
|