@rebind.gg/mcp-server 0.2.2 → 0.2.4

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rebind.gg/mcp-server",
3
- "version": "0.2.2",
3
+ "version": "0.2.4",
4
4
  "description": "MCP server exposing Rebind as computer-use tools: screenshot, verified click, type, key, scroll, and window control over the Remote Control protocol.",
5
5
  "type": "module",
6
6
  "bin": {
package/src/index.ts CHANGED
@@ -266,6 +266,17 @@ function assetsBlock(
266
266
  .join("\n\n");
267
267
  }
268
268
 
269
+ // The SDK as run_lua scripts see it: the typedef without its rb block, so the
270
+ // model writes the v1 names every relay version accepts (rb needs 3.9.0).
271
+ // src/rebind.d.luau is a byte-identical copy of packages/lua-sdk/types/
272
+ // rebind.d.luau shipped in the package; tests/lua-docs-sync.test.ts guards it.
273
+ async function v1Sdk(): Promise<string> {
274
+ const typedef = await Bun.file(
275
+ new URL("./rebind.d.luau", import.meta.url),
276
+ ).text();
277
+ return typedef.replace(/^-- rb\n[\s\S]*?(?=^-- v1 names\n)/m, "");
278
+ }
279
+
269
280
  // Server-level orientation (MCP `instructions`), built at startup. Every fact
270
281
  // is probed THROUGH the relay connection — process.platform is the wrong
271
282
  // signal because this process may run on a different machine than the relay.
@@ -300,9 +311,7 @@ async function buildInstructions(
300
311
  // MoveTo/Combo are synchronous and DO work in a one-shot chunk — so a mouse
301
312
  // click (e.g. ticking a checkbox) is scriptable as
302
313
  // `HID.MoveTo(x,y); HID.Down("Mouse1"); HID.Up("Mouse1")`.
303
- const sdk = await Bun.file(
304
- new URL("./rebind.d.luau", import.meta.url),
305
- ).text();
314
+ const sdk = await v1Sdk();
306
315
  const instructions = [
307
316
  `You control a real ${os} computer through Rebind. Keyboard shortcuts use ${modifierNote(os)}.`,
308
317
  clis
@@ -1146,10 +1155,7 @@ return { ok = Window.IsActive(h), name = name }
1146
1155
  {
1147
1156
  description: LUA_DOCS_DESC,
1148
1157
  },
1149
- // byte-identical copy of packages/lua-sdk/types/rebind.d.luau, shipped in
1150
- // the package; tests/lua-docs-sync.test.ts guards against drift
1151
- async () =>
1152
- text(await Bun.file(new URL("./rebind.d.luau", import.meta.url)).text()),
1158
+ async () => text(await v1Sdk()),
1153
1159
  );
1154
1160
 
1155
1161
  // Server-side subagents. Gated: a stock install ships without them. They make
package/src/rebind.d.luau CHANGED
@@ -1,18 +1,28 @@
1
- -- rebind sdk type definitions for luau language server
2
- -- provides autocomplete, hover docs, and type checking for all sdk namespaces.
3
- -- reference this file in your .luaurc: { "languageMode": "strict", "paths": ["types"] }
1
+ -- Rebind SDK type definitions for the Luau language server: autocomplete,
2
+ -- hover docs and type checking for every SDK function.
3
+ -- Reference this file in your .luaurc: { "languageMode": "strict", "paths": ["types"] }
4
+ --
5
+ -- The rb names need Rebind 3.9.0 or later; the v1 names work on every
6
+ -- version.
4
7
 
5
- -- handle types
8
+ -- handle types. A handle answers to its v1 method names on every version,
9
+ -- and to their snake_case forms from Rebind 3.9.0.
6
10
 
7
11
  export type TaskHandle = {
8
12
  Cancel: (self: TaskHandle) -> (),
9
13
  IsRunning: (self: TaskHandle) -> boolean,
14
+ stop: (self: TaskHandle) -> (),
15
+ is_running: (self: TaskHandle) -> boolean,
10
16
  }
11
17
 
12
18
  export type TimerHandle = {
13
19
  Cancel: (self: TimerHandle) -> (),
14
20
  Pause: (self: TimerHandle) -> (),
15
21
  Resume: (self: TimerHandle) -> (),
22
+ stop: (self: TimerHandle) -> (),
23
+ pause: (self: TimerHandle) -> (),
24
+ resume: (self: TimerHandle) -> (),
25
+ is_running: (self: TimerHandle) -> boolean,
16
26
  }
17
27
 
18
28
  export type MacroHandle = {
@@ -21,18 +31,34 @@ export type MacroHandle = {
21
31
  Resume: (self: MacroHandle) -> (),
22
32
  IsPlaying: (self: MacroHandle) -> boolean,
23
33
  GetProgress: (self: MacroHandle) -> number,
34
+ -- coroutine-only: waits until the macro finishes
35
+ Wait: (self: MacroHandle) -> (),
36
+ stop: (self: MacroHandle) -> (),
37
+ pause: (self: MacroHandle) -> (),
38
+ resume: (self: MacroHandle) -> (),
39
+ is_playing: (self: MacroHandle) -> boolean,
40
+ get_progress: (self: MacroHandle) -> number,
41
+ -- coroutine-only: waits until the macro finishes
42
+ wait: (self: MacroHandle) -> (),
24
43
  }
25
44
 
26
45
  export type BindHandle = {
27
46
  unbind: (self: BindHandle) -> (),
47
+ stop: (self: BindHandle) -> (),
28
48
  disable: (self: BindHandle) -> (),
29
49
  enable: (self: BindHandle) -> (),
30
50
  enabled: boolean,
31
51
  }
32
52
 
53
+ -- returned by rb.on (Rebind 3.9.0 and later); stop removes the handler
54
+ export type EventHandle = {
55
+ stop: (self: EventHandle) -> (),
56
+ }
57
+
33
58
  export type ServerHandle = {
34
59
  port: number,
35
60
  Stop: (self: ServerHandle) -> (),
61
+ stop: (self: ServerHandle) -> (),
36
62
  }
37
63
 
38
64
  -- UDP: socket handle returned by Net.UDPListen. SendTo sends from the bound
@@ -41,6 +67,8 @@ export type UdpHandle = {
41
67
  port: number,
42
68
  SendTo: (self: UdpHandle, ip: string, port: number, bytes: string) -> boolean,
43
69
  Stop: (self: UdpHandle) -> (),
70
+ send_to: (self: UdpHandle, ip: string, port: number, bytes: string) -> boolean,
71
+ stop: (self: UdpHandle) -> (),
44
72
  }
45
73
 
46
74
  -- WebSocket: server-side per-connection handle passed to OnConnect/OnMessage/OnClose.
@@ -50,6 +78,9 @@ export type WsClient = {
50
78
  Send: (self: WsClient, text: string) -> (),
51
79
  SendBinary: (self: WsClient, bytes: string) -> (),
52
80
  Close: (self: WsClient) -> (),
81
+ send: (self: WsClient, text: string) -> (),
82
+ send_binary: (self: WsClient, bytes: string) -> (),
83
+ close: (self: WsClient) -> (),
53
84
  }
54
85
 
55
86
  -- WebSocket: server handle returned by Net.WSListen.
@@ -59,6 +90,10 @@ export type WsServerHandle = {
59
90
  BroadcastBinary: (self: WsServerHandle, bytes: string) -> (),
60
91
  ClientCount: (self: WsServerHandle) -> number,
61
92
  Stop: (self: WsServerHandle) -> (),
93
+ broadcast: (self: WsServerHandle, text: string) -> (),
94
+ broadcast_binary: (self: WsServerHandle, bytes: string) -> (),
95
+ client_count: (self: WsServerHandle) -> number,
96
+ stop: (self: WsServerHandle) -> (),
62
97
  }
63
98
 
64
99
  -- WebSocket: outbound-connection handle returned by Net.WSConnect.
@@ -66,21 +101,32 @@ export type WsConnectHandle = {
66
101
  Send: (self: WsConnectHandle, text: string) -> (),
67
102
  SendBinary: (self: WsConnectHandle, bytes: string) -> (),
68
103
  Close: (self: WsConnectHandle) -> (),
104
+ send: (self: WsConnectHandle, text: string) -> (),
105
+ send_binary: (self: WsConnectHandle, bytes: string) -> (),
106
+ close: (self: WsConnectHandle) -> (),
69
107
  }
70
108
 
71
109
  -- WebSocket: server event-handler table passed to Net.WSListen.
72
110
  -- All fields are optional; handlers that are absent are never called.
111
+ -- From Rebind 3.9.0 each handler is also read under its snake_case name.
73
112
  export type WsListenHandlers = {
74
113
  OnConnect: ((client: WsClient) -> ())?,
75
114
  OnMessage: ((client: WsClient, payload: string, isBinary: boolean) -> ())?,
76
115
  OnClose: ((client: WsClient) -> ())?,
116
+ on_connect: ((client: WsClient) -> ())?,
117
+ on_message: ((client: WsClient, payload: string, is_binary: boolean) -> ())?,
118
+ on_close: ((client: WsClient) -> ())?,
77
119
  }
78
120
 
79
121
  -- WebSocket: client event-handler table passed to Net.WSConnect.
122
+ -- From Rebind 3.9.0 each handler is also read under its snake_case name.
80
123
  export type WsConnectHandlers = {
81
124
  OnOpen: (() -> ())?,
82
125
  OnMessage: ((payload: string, isBinary: boolean) -> ())?,
83
126
  OnClose: (() -> ())?,
127
+ on_open: (() -> ())?,
128
+ on_message: ((payload: string, is_binary: boolean) -> ())?,
129
+ on_close: (() -> ())?,
84
130
  }
85
131
 
86
132
  export type PipeHandle = {
@@ -90,6 +136,24 @@ export type PipeHandle = {
90
136
  Read: (self: PipeHandle) -> string?,
91
137
  Write: (self: PipeHandle, data: string) -> (),
92
138
  Close: (self: PipeHandle) -> (),
139
+ read: (self: PipeHandle) -> string?,
140
+ write: (self: PipeHandle, data: string) -> (),
141
+ close: (self: PipeHandle) -> (),
142
+ }
143
+
144
+ export type SoundHandle = {
145
+ Stop: (self: SoundHandle) -> (),
146
+ Pause: (self: SoundHandle) -> (),
147
+ Resume: (self: SoundHandle) -> (),
148
+ IsPlaying: (self: SoundHandle) -> boolean,
149
+ SetVolume: (self: SoundHandle, vol: number) -> (),
150
+ GetVolume: (self: SoundHandle) -> number,
151
+ stop: (self: SoundHandle) -> (),
152
+ pause: (self: SoundHandle) -> (),
153
+ resume: (self: SoundHandle) -> (),
154
+ is_playing: (self: SoundHandle) -> boolean,
155
+ set_volume: (self: SoundHandle, vol: number) -> (),
156
+ get_volume: (self: SoundHandle) -> number,
93
157
  }
94
158
 
95
159
  -- data types
@@ -178,6 +242,122 @@ export type HttpRequestOptions = {
178
242
  timeout: number?,
179
243
  }
180
244
 
245
+ export type ExecOptions = {
246
+ -- milliseconds; default 5000. The process is killed on expiry and Exec errors.
247
+ timeout: number?,
248
+ -- working directory for the command; default: the host process's cwd.
249
+ cwd: string?,
250
+ }
251
+
252
+ export type ExecResult = {
253
+ exit: number,
254
+ stdout: string,
255
+ stderr: string,
256
+ }
257
+
258
+ export type ExecDetachedOptions = {
259
+ cwd: string?,
260
+ }
261
+
262
+ -- a joypad state; every field is optional and defaults to neutral. buttons
263
+ -- lists pressed buttons 1-32, hat is 0 (up) to 7 clockwise (nil = centered),
264
+ -- x/y/z/rz are -1..1 (0 = center), slider and dial are 0..1.
265
+ export type JoypadState = {
266
+ buttons: { number }?,
267
+ hat: number?,
268
+ x: number?,
269
+ y: number?,
270
+ z: number?,
271
+ rz: number?,
272
+ slider: number?,
273
+ dial: number?,
274
+ }
275
+
276
+ export type ProcessEntry = {
277
+ pid: number,
278
+ name: string,
279
+ }
280
+
281
+ export type PixelMatch = {
282
+ x: number,
283
+ y: number,
284
+ }
285
+
286
+ export type MonitorEntry = {
287
+ index: number,
288
+ x: number,
289
+ y: number,
290
+ width: number,
291
+ height: number,
292
+ primary: boolean,
293
+ }
294
+
295
+ export type CaptureOptions = {
296
+ -- 1-based display index; default: the display under the cursor.
297
+ display: number?,
298
+ -- display-local logical coords; default: the whole display.
299
+ region: { x: number, y: number, w: number, h: number }?,
300
+ -- downscale so the long edge is at most this; default and 0: native.
301
+ max_edge: number?,
302
+ }
303
+
304
+ export type CaptureResult = {
305
+ image: string, -- base64 PNG
306
+ width: number,
307
+ height: number,
308
+ display: MonitorEntry,
309
+ cursor: { x: number, y: number },
310
+ }
311
+
312
+ export type FindImageOptions = {
313
+ display: number?,
314
+ region: { x: number, y: number, w: number, h: number }?,
315
+ -- 0.0 exact .. 1.0, per-channel fraction of 255 (same scale as SearchForColor).
316
+ tolerance: number?,
317
+ -- ratios to resample the needle by before scanning, tried in order; default { 1 }.
318
+ scales: { number }?,
319
+ }
320
+
321
+ export type ImageMatch = {
322
+ x: number,
323
+ y: number,
324
+ width: number,
325
+ height: number,
326
+ }
327
+
328
+ export type AppInfo = {
329
+ name: string,
330
+ pid: number,
331
+ }
332
+
333
+ export type PlayOptions = {
334
+ volume: number?,
335
+ loop: boolean?,
336
+ }
337
+
338
+ export type DialogMessageOptions = {
339
+ title: string?,
340
+ level: string?,
341
+ }
342
+
343
+ export type DialogFileFilter = {
344
+ name: string,
345
+ extensions: { string },
346
+ }
347
+
348
+ export type DialogFileOptions = {
349
+ title: string?,
350
+ location: string?,
351
+ filters: { DialogFileFilter }?,
352
+ }
353
+
354
+ export type RegexMatch = {
355
+ match: string,
356
+ captures: { string },
357
+ start: number,
358
+ finish: number,
359
+ }
360
+
181
361
  -- macro action types
182
362
 
183
363
  export type MacroMoveAction = {
@@ -186,6 +366,8 @@ export type MacroMoveAction = {
186
366
  delay: number,
187
367
  }
188
368
 
369
+ -- from Rebind 3.9.0, holdMs and charDelay are also read as hold_ms and
370
+ -- char_delay
189
371
  export type MacroExplicitAction = {
190
372
  action: string,
191
373
  code: string?,
@@ -197,12 +379,15 @@ export type MacroExplicitAction = {
197
379
  amount: number?,
198
380
  holdMs: number?,
199
381
  charDelay: number?,
382
+ hold_ms: number?,
383
+ char_delay: number?,
200
384
  delay: number,
201
385
  }
202
386
 
203
387
  export type MacroAction = MacroMoveAction | MacroExplicitAction
204
388
 
205
- -- ui widget option types
389
+ -- ui widget option types; from Rebind 3.9.0, showIf and maxLength are also
390
+ -- read as show_if and max_length
206
391
 
207
392
  export type WidgetOpts = {
208
393
  label: string?,
@@ -210,6 +395,7 @@ export type WidgetOpts = {
210
395
  group: string?,
211
396
  tab: string?,
212
397
  showIf: string?,
398
+ show_if: string?,
213
399
  }
214
400
 
215
401
  export type ToggleOpts = WidgetOpts
@@ -228,6 +414,7 @@ export type SelectOpts = WidgetOpts
228
414
  export type TextOpts = WidgetOpts & {
229
415
  placeholder: string?,
230
416
  maxLength: number?,
417
+ max_length: number?,
231
418
  }
232
419
 
233
420
  export type ColorOpts = WidgetOpts
@@ -264,6 +451,399 @@ export type RebindInfo = {
264
451
  platform: string,
265
452
  }
266
453
 
454
+ -- options of the rb calls that take one options table (Rebind 3.9.0 and later)
455
+
456
+ export type RbHttpOptions = {
457
+ headers: { [string]: string }?,
458
+ timeout: number?,
459
+ }
460
+
461
+ export type RbPlayOptions = {
462
+ -- playback speed multiplier
463
+ speed: number?,
464
+ -- "parallel" (default, plays alongside running macros) | "replace"
465
+ -- (stops all running macros first)
466
+ mode: string?,
467
+ }
468
+
469
+ export type RbExecDetachedOptions = {
470
+ args: { string }?,
471
+ cwd: string?,
472
+ }
473
+
474
+ -- fields left out keep their current value
475
+ export type RbWindowRect = {
476
+ x: number?,
477
+ y: number?,
478
+ width: number?,
479
+ height: number?,
480
+ }
481
+
482
+ export type RbBindOptions = {
483
+ -- false lets the key through to the system; default: the key is taken
484
+ swallow: boolean?,
485
+ -- checked on each press; false skips the bind for that press
486
+ when: ((device: InputDevice?) -> boolean)?,
487
+ -- runs when the key is released
488
+ release: ((device: InputDevice?) -> ())?,
489
+ -- nickname or id: the bind answers only to that device
490
+ device: string?,
491
+ }
492
+
493
+ -- events for rb.on (Rebind 3.9.0 and later), with the arguments each
494
+ -- handler receives. For down, up, move and scroll, a handler that returns
495
+ -- false swallows the input.
496
+ export type Event =
497
+ | "start" -- ()
498
+ | "stop" -- ()
499
+ | "focus" -- (window: WindowInfo)
500
+ | "blur" -- (window: WindowInfo)
501
+ | "tick" -- (dt_ms: number)
502
+ | "error" -- (message: string)
503
+ | "reload" -- ()
504
+ | "down" -- (key: string, device: InputDevice?)
505
+ | "up" -- (key: string, duration_ms: number, device: InputDevice?)
506
+ | "move" -- (dx: number, dy: number, device: InputDevice?) hardware mode only
507
+ | "scroll" -- (delta: number, device: InputDevice?)
508
+ | "scroll_h" -- (delta: number)
509
+ | "input_down" -- (key: string, device: InputDevice?)
510
+ | "input_up" -- (key: string, duration_ms: number, device: InputDevice?)
511
+ | "input_move" -- (dx: number, dy: number, device: InputDevice?)
512
+ | "input_scroll" -- (delta: number, device: InputDevice?)
513
+
514
+ -- valid key names for input events and binds:
515
+ --
516
+ -- letters: A-Z
517
+ -- numbers: 0-9
518
+ -- function: F1-F12
519
+ -- mouse: Mouse1 (left), Mouse2 (right), Mouse3 (middle), Mouse4, Mouse5
520
+ -- modifiers: LCtrl, RCtrl, LShift, RShift, LAlt, RAlt, LWin, RWin
521
+ -- navigation: Up, Down, Left, Right, Home, End, PageUp, PageDown, Insert, Delete
522
+ -- editing: Backspace, Tab, Enter, Space, Escape
523
+ -- punctuation: Minus, Equal, LeftBrace, RightBrace, Backslash, Semicolon,
524
+ -- Apostrophe, Grave, Comma, Period, Slash
525
+ -- locks: CapsLock, ScrollLock, NumLock
526
+ -- system: PrintScreen, Pause, Menu
527
+ -- numpad: Kp0-Kp9, KpPlus, KpMinus, KpMultiply, KpDivide, KpEnter, KpDot
528
+ --
529
+ -- HID output only:
530
+ -- function: F13-F24
531
+ -- media: MediaNext, MediaPrev, MediaStop, MediaPlay, VolumeUp, VolumeDown, Mute
532
+ -- aliases: Numpad0-9, NP0-9, NPAdd, NPSubtract, NPMultiply, NPDivide, NPEnter,
533
+ -- NPDecimal, Ctrl, Shift, Alt, Win, Meta, Del, PgUp, PgDn, etc.
534
+
535
+ -- rb
536
+ -- The scripting surface, for scripts that declare min_sdk=3.9.0 or later.
537
+ -- Every name is the snake_case of a v1 name (HID.MoveTo is rb.hid.move_to).
538
+ -- Hooks are rb.on(event, fn) and key names passed to rb ignore case.
539
+
540
+ declare rb: {
541
+ -- runtime identity: script name, versions, platform
542
+ info: RebindInfo,
543
+ -- runs fn as a coroutine, where rb.sleep and other coroutine-only calls work
544
+ run: (fn: () -> ()) -> TaskHandle,
545
+ -- coroutine-only: call inside rb.run
546
+ sleep: (ms: number) -> (),
547
+ after: (ms: number, fn: () -> ()) -> TaskHandle,
548
+ -- wraps fn so it runs on the coroutine scheduler; when auto-spawned the call
549
+ -- returns false instead of fn's results.
550
+ async: (fn: (...any) -> ...any) -> (...any) -> ...any,
551
+ -- adds a handler for an event; handlers run in the order they were added.
552
+ -- returns a handle whose stop removes the handler.
553
+ on: (event: Event, fn: (...any) -> boolean?) -> EventHandle,
554
+ -- rb.bind(key, fn, options) takes the key unless options.swallow is false.
555
+ -- the callback's return value is ignored.
556
+ bind: typeof(setmetatable({} :: {
557
+ toggle: (key: string, ui_id: string) -> BindHandle,
558
+ remap: (from: string, to: string) -> BindHandle,
559
+ }, {} :: {
560
+ __call: (self: any, key: string, fn: ((device: InputDevice?) -> ...any)?, options: RbBindOptions?) -> BindHandle,
561
+ })),
562
+ -- each level takes any number of values, joined like print
563
+ log: {
564
+ info: (...any) -> (),
565
+ warn: (...any) -> (),
566
+ error: (...any) -> (),
567
+ debug: (...any) -> (),
568
+ },
569
+ system: {
570
+ time: () -> number,
571
+ mouse: () -> (number, number),
572
+ screen: () -> (number, number),
573
+ window: () -> WindowInfo,
574
+ -- runs cmd through the shell (sh -c / cmd.exe), output capped at 64KB per
575
+ -- stream. Requires the 'exec' permission. Inside rb.run it runs off the
576
+ -- tick thread; elsewhere it blocks the tick thread and warns once.
577
+ exec: (cmd: string, options: ExecOptions?) -> ExecResult,
578
+ -- fire-and-forget spawn (no shell, argv array); returns the pid.
579
+ exec_detached: (cmd: string, options: RbExecDetachedOptions?) -> number,
580
+ -- types text through the host OS (Unicode aware), never through the HID
581
+ -- transport. raises when the OS refuses the injection.
582
+ type_text: (text: string) -> (),
583
+ },
584
+ hid: {
585
+ down: (code: string) -> (),
586
+ up: (code: string) -> (),
587
+ -- coroutine-only (sleeps for hold_ms): call inside rb.run. From a plain
588
+ -- chunk use down and up, or combo.
589
+ press: (code: string, hold_ms: number?) -> (),
590
+ -- fires a chord in one synchronous call (press left to right, release
591
+ -- right to left). modifier tokens accept macOS aliases: "cmd",
592
+ -- "opt"/"option", plus "ctrl"/"shift"/"alt"/"win".
593
+ combo: (code: string) -> (),
594
+ -- instant whole-string insertion; typewriter types one character at a
595
+ -- time and is coroutine-only.
596
+ type: (text: string) -> (),
597
+ typewriter: (text: string, delay_ms: number?) -> (),
598
+ move: (dx: number, dy: number) -> (),
599
+ move_to: (x: number, y: number) -> (),
600
+ scroll: (amount: number) -> (),
601
+ set_mouse_mode: (mode: string) -> (),
602
+ get_mouse_mode: () -> string,
603
+ -- keyboard HID usage for a key name, or nil (media keys return nil). down,
604
+ -- up, press and combo also accept "usage:N" (decimal or 0x hex, 0x04-0xE7).
605
+ usage: (name: string) -> number?,
606
+ -- sets the whole joypad state (last write wins across scripts). Needs a
607
+ -- Rebind Link whose USB profile has a joypad; raises otherwise.
608
+ joypad: (state: JoypadState?) -> (),
609
+ -- back to neutral; the relay also does this when a script stops or errors
610
+ joypad_reset: () -> (),
611
+ },
612
+ input: {
613
+ is_down: (code: string) -> boolean,
614
+ duration: (code: string) -> number,
615
+ keys: () -> { string },
616
+ mouse_pos: () -> MousePos,
617
+ modifiers: () -> ModifierState,
618
+ },
619
+ ui: {
620
+ schema: (descriptors: { [string]: any }) -> UiProxy,
621
+ toggle: (default: boolean, opts: ToggleOpts?) -> any,
622
+ slider: (default: number, opts: SliderOpts?) -> any,
623
+ keybind: (default: string, opts: KeybindOpts?) -> any,
624
+ select: (default: string, choices: { string }, opts: SelectOpts?) -> any,
625
+ text: (default: string, opts: TextOpts?) -> any,
626
+ color: (default: string, opts: ColorOpts?) -> any,
627
+ get: (id: string) -> any,
628
+ set: (id: string, value: any) -> (),
629
+ get_all: () -> { [string]: any },
630
+ get_schema: () -> { any },
631
+ notify: (text: string, variant: string?) -> (),
632
+ },
633
+ timer: {
634
+ after: (ms: number, callback: () -> ()) -> TimerHandle,
635
+ every: (ms: number, callback: () -> ()) -> TimerHandle,
636
+ stop_all: () -> (),
637
+ },
638
+ macro: {
639
+ play: (macro: { MacroAction }, options: RbPlayOptions?) -> MacroHandle,
640
+ stop_all: () -> (),
641
+ record: (options: RecordOpts?) -> (),
642
+ finish: () -> { MacroAction },
643
+ -- the saved-macro format: a `macro=true` modeline, then `return` of the
644
+ -- actions. require() of a saved macro returns the actions.
645
+ to_script: (macro: { MacroAction }, name: string) -> string,
646
+ stream: (macro: { MacroAction }) -> (),
647
+ abort: () -> (),
648
+ },
649
+ math: {
650
+ random: (min: number, max: number) -> number,
651
+ gaussian: (mean: number, std_dev: number) -> number,
652
+ scale: (macro: { MacroAction }, x_factor: number, y_factor: number) -> { MacroAction },
653
+ spline: (macro: { MacroAction }, tension: number) -> { MacroAction },
654
+ resample: (macro: { MacroAction }, interval_ms: number) -> { MacroAction },
655
+ time_comp: (macro: { MacroAction }, target_ms: number) -> { MacroAction },
656
+ -- mode: "linear" | anything else = positional curve (default).
657
+ interpolate: (macro: { MacroAction }, interval_ms: number, mode: string?) -> { MacroAction },
658
+ },
659
+ json: {
660
+ parse: (s: string) -> any,
661
+ stringify: (value: any) -> string,
662
+ },
663
+ process: {
664
+ exists: (name: string) -> number?,
665
+ list: (name: string?) -> { ProcessEntry },
666
+ kill: (pid: number) -> boolean,
667
+ },
668
+ -- pixel reads are limited by a screen:target or screen:none permission entry
669
+ screen: {
670
+ pixel_hex: (x: number?, y: number?) -> string,
671
+ -- reads the published frame. regions up to 200k physical pixels answer on
672
+ -- the calling thread; larger ones yield inside rb.run and are refused
673
+ -- outside it.
674
+ search_color: (region: { number }, color: string, tolerance: number?) -> PixelMatch?,
675
+ list: () -> { MonitorEntry },
676
+ -- yields inside rb.run; refused outside a coroutine.
677
+ capture: (options: CaptureOptions?) -> CaptureResult,
678
+ -- needle is a PNG path relative to the script directory. yields inside
679
+ -- rb.run; refused outside a coroutine.
680
+ find_image: (needle: string, options: FindImageOptions?) -> ImageMatch?,
681
+ -- usable desktop of the display containing the point (cursor when omitted).
682
+ -- Windows only; raises elsewhere.
683
+ work_area: (x: number?, y: number?) -> WindowPos,
684
+ },
685
+ window: {
686
+ find: (title: string) -> number?,
687
+ list: (title: string?) -> { WindowEntry },
688
+ get_title: (handle: number?) -> string,
689
+ get_class: (handle: number?) -> string,
690
+ get_pos: (handle: number?) -> WindowPos,
691
+ get_pid: (handle: number?) -> number,
692
+ activate: (handle: number) -> (),
693
+ move: (handle: number, rect: RbWindowRect?) -> (),
694
+ set_always_on_top: (handle: number, enabled: boolean) -> (),
695
+ set_transparency: (handle: number, alpha: number) -> (),
696
+ set_title: (handle: number, title: string) -> (),
697
+ minimize: (handle: number?) -> (),
698
+ maximize: (handle: number?) -> (),
699
+ restore: (handle: number?) -> (),
700
+ hide: (handle: number?) -> (),
701
+ show: (handle: number?) -> (),
702
+ close: (handle: number?) -> (),
703
+ kill: (handle: number?) -> (),
704
+ is_visible: (handle: number) -> boolean,
705
+ is_active: (handle: number?) -> boolean,
706
+ get_foreground: () -> number?,
707
+ from_point: (x: number, y: number) -> number?,
708
+ -- "minimized" | "maximized" | "normal". Windows only; raises elsewhere.
709
+ get_state: (handle: number?) -> string,
710
+ wait: (title: string, timeout: number?) -> number?,
711
+ wait_active: (title: string, timeout: number?) -> boolean,
712
+ wait_close: (title: string, timeout: number?) -> boolean,
713
+ },
714
+ -- application control. macOS matches app display names ignoring case;
715
+ -- Windows matches executable names (".exe" optional) and cannot hide;
716
+ -- Linux raises on every call.
717
+ app: {
718
+ front: () -> AppInfo?,
719
+ is_front: (name: string) -> boolean,
720
+ is_running: (name: string) -> boolean,
721
+ is_hidden: (name: string) -> boolean,
722
+ activate: (name: string) -> (),
723
+ hide: (name: string) -> (),
724
+ quit: (name: string) -> (),
725
+ },
726
+ -- permission: net, or net:<host> for the client calls and ws_connect
727
+ net: {
728
+ get: (url: string, options: RbHttpOptions?) -> HttpResponse,
729
+ post: (url: string, body: string, options: RbHttpOptions?) -> HttpResponse,
730
+ put: (url: string, body: string, options: RbHttpOptions?) -> HttpResponse,
731
+ patch: (url: string, body: string, options: RbHttpOptions?) -> HttpResponse,
732
+ delete: (url: string, options: RbHttpOptions?) -> HttpResponse,
733
+ head: (url: string, options: RbHttpOptions?) -> HttpResponse,
734
+ request: (options: HttpRequestOptions) -> HttpResponse,
735
+ listen: (port: number, handler: (request: HttpRequest) -> HttpResponseSpec | string) -> ServerHandle,
736
+ ws_listen: (port: number, handlers: WsListenHandlers) -> WsServerHandle,
737
+ ws_connect: (url: string, handlers: WsConnectHandlers) -> WsConnectHandle,
738
+ -- binds a UDP socket; handler receives each datagram's raw bytes and sender.
739
+ udp_listen: (bind_ip: string, port: number, handler: (data: string, ip: string, port: number) -> ()) -> UdpHandle,
740
+ -- this computer's IPv4 address on its default route, or nil when there is none.
741
+ local_ip: () -> string?,
742
+ },
743
+ pipe: {
744
+ open: (name: string, options: { size: number? }?) -> PipeHandle,
745
+ },
746
+ -- paths are relative to the script directory; absolute paths are rejected
747
+ file: {
748
+ read: (path: string) -> string,
749
+ read_bytes: (path: string) -> string,
750
+ write: (path: string, content: string) -> (),
751
+ append: (path: string, content: string) -> (),
752
+ exists: (path: string) -> boolean,
753
+ delete: (path: string) -> boolean,
754
+ -- nil path lists the script's own directory
755
+ list: (path: string?) -> { string },
756
+ mkdir: (path: string) -> boolean,
757
+ -- recursive; returns true even when the directory did not exist.
758
+ rmdir: (path: string) -> boolean,
759
+ copy: (src: string, dst: string) -> (),
760
+ move: (src: string, dst: string) -> (),
761
+ read_json: (path: string) -> any,
762
+ write_json: (path: string, value: any) -> (),
763
+ script_dir: () -> string,
764
+ size: (path: string) -> number,
765
+ time: (path: string) -> number,
766
+ is_dir: (path: string) -> boolean,
767
+ is_file: (path: string) -> boolean,
768
+ },
769
+ audio: {
770
+ beep: () -> (),
771
+ play: (path: string, options: PlayOptions?) -> SoundHandle,
772
+ stop_all: () -> (),
773
+ set_master_volume: (vol: number) -> (),
774
+ get_master_volume: () -> number,
775
+ },
776
+ -- every dialog call is coroutine-only: it yields until the dialog closes
777
+ -- and errors outside rb.run. the pickers return nil on cancel.
778
+ dialog: {
779
+ message: (text: string, options: DialogMessageOptions?) -> (),
780
+ confirm: (text: string, options: DialogMessageOptions?) -> boolean,
781
+ open_file: (options: DialogFileOptions?) -> string?,
782
+ open_dir: (options: DialogFileOptions?) -> string?,
783
+ save_file: (options: DialogFileOptions?) -> string?,
784
+ },
785
+ -- limited by clipboard:read, clipboard:write, clipboard:none
786
+ clipboard: {
787
+ get: () -> string?,
788
+ set: (text: string) -> (),
789
+ },
790
+ script: {
791
+ exit: (reason: string?) -> (),
792
+ reload: () -> (),
793
+ },
794
+ regex: {
795
+ is_match: (text: string, pattern: string) -> boolean,
796
+ find: (text: string, pattern: string) -> RegexMatch?,
797
+ find_all: (text: string, pattern: string) -> { RegexMatch },
798
+ replace: (text: string, pattern: string, replacement: string) -> string,
799
+ replace_all: (text: string, pattern: string, replacement: string) -> string,
800
+ split: (text: string, pattern: string) -> { string },
801
+ },
802
+ config: {
803
+ parse_toml: (text: string) -> { [string]: any },
804
+ to_toml: (value: { [string]: any }) -> string,
805
+ read_toml: (path: string) -> { [string]: any },
806
+ write_toml: (path: string, value: { [string]: any }) -> (),
807
+ },
808
+ hash: {
809
+ md5: (data: string) -> string,
810
+ sha1: (data: string) -> string,
811
+ sha256: (data: string) -> string,
812
+ sha512: (data: string) -> string,
813
+ crc32: (data: string) -> number,
814
+ hmac: (algo: string, key: string, data: string) -> string,
815
+ },
816
+ codec: {
817
+ base64: (data: string) -> string,
818
+ base64_decode: (str: string) -> string,
819
+ hex: (data: string) -> string,
820
+ hex_decode: (str: string) -> string,
821
+ },
822
+ env: {
823
+ get: (name: string) -> string?,
824
+ set: (name: string, value: string) -> (),
825
+ home: () -> string?,
826
+ config: () -> string?,
827
+ data: () -> string?,
828
+ desktop: () -> string?,
829
+ documents: () -> string?,
830
+ downloads: () -> string?,
831
+ appdata: () -> string?,
832
+ temp: () -> string?,
833
+ },
834
+ -- Windows only
835
+ registry: {
836
+ read: (key_path: string, value_name: string) -> string | number,
837
+ write: (key_path: string, type: string, value_name: string, value: any) -> (),
838
+ delete_value: (key_path: string, value_name: string) -> (),
839
+ delete_key: (key_path: string) -> (),
840
+ create_key: (key_path: string) -> (),
841
+ },
842
+ }
843
+
844
+ -- v1 names
845
+ -- The names every Rebind version accepts. They keep working.
846
+
267
847
  -- global functions
268
848
 
269
849
  declare function Run(fn: () -> ()): TaskHandle
@@ -284,23 +864,6 @@ declare _REBIND: RebindInfo
284
864
 
285
865
  -- system namespace
286
866
 
287
- export type ExecOptions = {
288
- -- milliseconds; default 5000. The process is killed on expiry and Exec errors.
289
- timeout: number?,
290
- -- working directory for the command; default: the host process's cwd.
291
- cwd: string?,
292
- }
293
-
294
- export type ExecResult = {
295
- exit: number,
296
- stdout: string,
297
- stderr: string,
298
- }
299
-
300
- export type ExecDetachedOptions = {
301
- cwd: string?,
302
- }
303
-
304
867
  declare System: {
305
868
  Time: () -> number,
306
869
  Mouse: () -> (number, number),
@@ -322,20 +885,6 @@ declare System: {
322
885
 
323
886
  -- hid namespace
324
887
 
325
- -- a joypad state; every field is optional and defaults to neutral. buttons
326
- -- lists pressed buttons 1-32, hat is 0 (up) to 7 clockwise (nil = centered),
327
- -- x/y/z/rz are -1..1 (0 = center), slider and dial are 0..1.
328
- export type JoypadState = {
329
- buttons: { number }?,
330
- hat: number?,
331
- x: number?,
332
- y: number?,
333
- z: number?,
334
- rz: number?,
335
- slider: number?,
336
- dial: number?,
337
- }
338
-
339
888
  declare HID: {
340
889
  Down: (code: string) -> (),
341
890
  Up: (code: string) -> (),
@@ -457,11 +1006,6 @@ declare JSON: {
457
1006
 
458
1007
  -- process namespace
459
1008
 
460
- export type ProcessEntry = {
461
- pid: number,
462
- name: string,
463
- }
464
-
465
1009
  declare Process: {
466
1010
  Exists: (name: string) -> number?,
467
1011
  List: (name: string?) -> { ProcessEntry },
@@ -470,52 +1014,7 @@ declare Process: {
470
1014
 
471
1015
  -- screen namespace
472
1016
 
473
- export type PixelMatch = {
474
- x: number,
475
- y: number,
476
- }
477
-
478
- export type MonitorEntry = {
479
- index: number,
480
- x: number,
481
- y: number,
482
- width: number,
483
- height: number,
484
- primary: boolean,
485
- }
486
-
487
- export type CaptureOptions = {
488
- -- 1-based display index; default: the display under the cursor.
489
- display: number?,
490
- -- display-local logical coords; default: the whole display.
491
- region: { x: number, y: number, w: number, h: number }?,
492
- -- downscale so the long edge is at most this; default and 0: native.
493
- max_edge: number?,
494
- }
495
-
496
- export type CaptureResult = {
497
- image: string, -- base64 PNG
498
- width: number,
499
- height: number,
500
- display: MonitorEntry,
501
- cursor: { x: number, y: number },
502
- }
503
-
504
- export type FindImageOptions = {
505
- display: number?,
506
- region: { x: number, y: number, w: number, h: number }?,
507
- -- 0.0 exact .. 1.0, per-channel fraction of 255 (same scale as SearchForColor).
508
- tolerance: number?,
509
- -- ratios to resample the needle by before scanning, tried in order; default { 1 }.
510
- scales: { number }?,
511
- }
512
-
513
- export type ImageMatch = {
514
- x: number,
515
- y: number,
516
- width: number,
517
- height: number,
518
- }
1017
+ -- pixel reads are limited by a screen:target or screen:none permission entry
519
1018
 
520
1019
  declare Screen: {
521
1020
  GetPixelColor: (x: number?, y: number?) -> string,
@@ -572,11 +1071,6 @@ declare Window: {
572
1071
  -- Activate focuses a running app and does not launch one; Quit closes its
573
1072
  -- windows; Hide/IsHidden raise. Linux: every call raises.
574
1073
 
575
- export type AppInfo = {
576
- name: string,
577
- pid: number,
578
- }
579
-
580
1074
  declare App: {
581
1075
  Front: () -> AppInfo?,
582
1076
  IsFront: (name: string) -> boolean,
@@ -587,7 +1081,7 @@ declare App: {
587
1081
  Quit: (name: string) -> (),
588
1082
  }
589
1083
 
590
- -- net namespace
1084
+ -- net namespace (permission: net, or net:<host> for the client calls and WSConnect)
591
1085
 
592
1086
  declare Net: {
593
1087
  Get: (url: string, headers: { [string]: string }?, options: HttpOptions?) -> HttpResponse,
@@ -640,20 +1134,6 @@ declare File: {
640
1134
 
641
1135
  -- audio namespace
642
1136
 
643
- export type PlayOptions = {
644
- volume: number?,
645
- loop: boolean?,
646
- }
647
-
648
- export type SoundHandle = {
649
- Stop: (self: SoundHandle) -> (),
650
- Pause: (self: SoundHandle) -> (),
651
- Resume: (self: SoundHandle) -> (),
652
- IsPlaying: (self: SoundHandle) -> boolean,
653
- SetVolume: (self: SoundHandle, vol: number) -> (),
654
- GetVolume: (self: SoundHandle) -> number,
655
- }
656
-
657
1137
  declare Audio: {
658
1138
  Beep: () -> (),
659
1139
  Play: (path: string, options: PlayOptions?) -> SoundHandle,
@@ -664,22 +1144,6 @@ declare Audio: {
664
1144
 
665
1145
  -- dialog namespace
666
1146
 
667
- export type DialogMessageOptions = {
668
- title: string?,
669
- level: string?,
670
- }
671
-
672
- export type DialogFileFilter = {
673
- name: string,
674
- extensions: { string },
675
- }
676
-
677
- export type DialogFileOptions = {
678
- title: string?,
679
- location: string?,
680
- filters: { DialogFileFilter }?,
681
- }
682
-
683
1147
  -- every Dialog call is coroutine-only: it yields until the dialog closes and
684
1148
  -- errors outside Run(). the file and folder pickers return nil on cancel.
685
1149
  declare Dialog: {
@@ -690,7 +1154,7 @@ declare Dialog: {
690
1154
  SaveFile: (options: DialogFileOptions?) -> string?,
691
1155
  }
692
1156
 
693
- -- clipboard namespace
1157
+ -- clipboard namespace (limited by clipboard:read, clipboard:write, clipboard:none)
694
1158
 
695
1159
  declare Clipboard: {
696
1160
  Get: () -> string?,
@@ -706,13 +1170,6 @@ declare Script: {
706
1170
 
707
1171
  -- regex namespace
708
1172
 
709
- export type RegexMatch = {
710
- match: string,
711
- captures: { string },
712
- start: number,
713
- finish: number,
714
- }
715
-
716
1173
  declare Regex: {
717
1174
  IsMatch: (text: string, pattern: string) -> boolean,
718
1175
  Find: (text: string, pattern: string) -> RegexMatch?,
@@ -776,27 +1233,6 @@ declare Registry: {
776
1233
  CreateKey: (keyPath: string) -> (),
777
1234
  }
778
1235
 
779
- -- valid key names for input hooks (OnDown, OnUp):
780
- --
781
- -- letters: A-Z
782
- -- numbers: 0-9
783
- -- function: F1-F12
784
- -- mouse: Mouse1 (left), Mouse2 (right), Mouse3 (middle), Mouse4, Mouse5
785
- -- modifiers: LCtrl, RCtrl, LShift, RShift, LAlt, RAlt, LWin, RWin
786
- -- navigation: Up, Down, Left, Right, Home, End, PageUp, PageDown, Insert, Delete
787
- -- editing: Backspace, Tab, Enter, Space, Escape
788
- -- punctuation: Minus, Equal, LeftBrace, RightBrace, Backslash, Semicolon,
789
- -- Apostrophe, Grave, Comma, Period, Slash
790
- -- locks: CapsLock, ScrollLock, NumLock
791
- -- system: PrintScreen, Pause, Menu
792
- -- numpad: Kp0-Kp9, KpPlus, KpMinus, KpMultiply, KpDivide, KpEnter, KpDot
793
- --
794
- -- HID output only (HID.Down, HID.Press, etc.):
795
- -- function: F13-F24
796
- -- media: MediaNext, MediaPrev, MediaStop, MediaPlay, VolumeUp, VolumeDown, Mute
797
- -- aliases: Numpad0-9, NP0-9, NPAdd, NPSubtract, NPMultiply, NPDivide, NPEnter,
798
- -- NPDecimal, Ctrl, Shift, Alt, Win, Meta, Del, PgUp, PgDn, etc.
799
-
800
1236
  -- hooks: declare these as global functions in your script.
801
1237
  -- uncomment the ones you use; the runtime calls them when events occur.
802
1238