@moxxy/plugin-computer-control 0.26.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/dist/index.d.ts +30 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +43 -0
- package/dist/index.js.map +1 -0
- package/dist/shell.d.ts +56 -0
- package/dist/shell.d.ts.map +1 -0
- package/dist/shell.js +189 -0
- package/dist/shell.js.map +1 -0
- package/dist/tools/applescript.d.ts +2 -0
- package/dist/tools/applescript.d.ts.map +1 -0
- package/dist/tools/applescript.js +38 -0
- package/dist/tools/applescript.js.map +1 -0
- package/dist/tools/click.d.ts +2 -0
- package/dist/tools/click.d.ts.map +1 -0
- package/dist/tools/click.js +50 -0
- package/dist/tools/click.js.map +1 -0
- package/dist/tools/clipboard.d.ts +2 -0
- package/dist/tools/clipboard.d.ts.map +1 -0
- package/dist/tools/clipboard.js +62 -0
- package/dist/tools/clipboard.js.map +1 -0
- package/dist/tools/key.d.ts +11 -0
- package/dist/tools/key.d.ts.map +1 -0
- package/dist/tools/key.js +135 -0
- package/dist/tools/key.js.map +1 -0
- package/dist/tools/open.d.ts +2 -0
- package/dist/tools/open.d.ts.map +1 -0
- package/dist/tools/open.js +76 -0
- package/dist/tools/open.js.map +1 -0
- package/dist/tools/screenshot.d.ts +2 -0
- package/dist/tools/screenshot.d.ts.map +1 -0
- package/dist/tools/screenshot.js +162 -0
- package/dist/tools/screenshot.js.map +1 -0
- package/dist/tools/type.d.ts +8 -0
- package/dist/tools/type.d.ts.map +1 -0
- package/dist/tools/type.js +57 -0
- package/dist/tools/type.js.map +1 -0
- package/package.json +65 -0
- package/skills/computer-control.md +159 -0
- package/src/index.ts +55 -0
- package/src/shell.test.ts +186 -0
- package/src/shell.ts +213 -0
- package/src/tools/applescript-serialize.test.ts +74 -0
- package/src/tools/applescript.ts +41 -0
- package/src/tools/click.ts +52 -0
- package/src/tools/clipboard.ts +63 -0
- package/src/tools/key.ts +147 -0
- package/src/tools/open.ts +81 -0
- package/src/tools/screenshot.ts +181 -0
- package/src/tools/type.ts +60 -0
- package/src/tools.test.ts +128 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Moxxy (moxxy.ai)
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { type Plugin, type ToolDef } from '@moxxy/sdk';
|
|
2
|
+
import { IS_DARWIN } from './shell.js';
|
|
3
|
+
import { applescriptTool } from './tools/applescript.js';
|
|
4
|
+
import { clickTool } from './tools/click.js';
|
|
5
|
+
import { clipboardTool } from './tools/clipboard.js';
|
|
6
|
+
import { keyTool } from './tools/key.js';
|
|
7
|
+
import { openTool } from './tools/open.js';
|
|
8
|
+
import { screenshotTool } from './tools/screenshot.js';
|
|
9
|
+
import { typeTool } from './tools/type.js';
|
|
10
|
+
export { applescriptTool, clickTool, clipboardTool, keyTool, openTool, screenshotTool, typeTool, };
|
|
11
|
+
export declare const computerControlTools: ReadonlyArray<ToolDef>;
|
|
12
|
+
/**
|
|
13
|
+
* `@moxxy/plugin-computer-control` — programmatic control of the host
|
|
14
|
+
* computer (mouse, keyboard, screenshot, clipboard, app launching,
|
|
15
|
+
* AppleScript escape hatch).
|
|
16
|
+
*
|
|
17
|
+
* Currently macOS-only: every tool shells out to built-in binaries
|
|
18
|
+
* (`screencapture`, `osascript`, `open`, `pbpaste`, `pbcopy`). On any
|
|
19
|
+
* other platform the plugin still registers — the tools' handlers
|
|
20
|
+
* throw a clear "macOS only" error — so the model's tool list stays
|
|
21
|
+
* stable across hosts (avoids "tool disappeared on Linux" confusion).
|
|
22
|
+
*
|
|
23
|
+
* Every tool is `permission: 'prompt'`. There is intentionally no
|
|
24
|
+
* "allow always" shortcut for these — granting blanket permission to
|
|
25
|
+
* drive the user's screen + keyboard is exactly the wrong default.
|
|
26
|
+
*/
|
|
27
|
+
export declare const computerControlPlugin: Plugin;
|
|
28
|
+
export default computerControlPlugin;
|
|
29
|
+
export { IS_DARWIN };
|
|
30
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgB,KAAK,MAAM,EAAE,KAAK,OAAO,EAAE,MAAM,YAAY,CAAC;AACrE,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AACvC,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AACzD,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC7C,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAC;AACzC,OAAO,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAC3C,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAE3C,OAAO,EACL,eAAe,EACf,SAAS,EACT,aAAa,EACb,OAAO,EACP,QAAQ,EACR,cAAc,EACd,QAAQ,GACT,CAAC;AAEF,eAAO,MAAM,oBAAoB,EAAE,aAAa,CAAC,OAAO,CAQvD,CAAC;AAEF;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,qBAAqB,EAAE,MAIlC,CAAC;AAEH,eAAe,qBAAqB,CAAC;AAGrC,OAAO,EAAE,SAAS,EAAE,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { definePlugin } from '@moxxy/sdk';
|
|
2
|
+
import { IS_DARWIN } from './shell.js';
|
|
3
|
+
import { applescriptTool } from './tools/applescript.js';
|
|
4
|
+
import { clickTool } from './tools/click.js';
|
|
5
|
+
import { clipboardTool } from './tools/clipboard.js';
|
|
6
|
+
import { keyTool } from './tools/key.js';
|
|
7
|
+
import { openTool } from './tools/open.js';
|
|
8
|
+
import { screenshotTool } from './tools/screenshot.js';
|
|
9
|
+
import { typeTool } from './tools/type.js';
|
|
10
|
+
export { applescriptTool, clickTool, clipboardTool, keyTool, openTool, screenshotTool, typeTool, };
|
|
11
|
+
export const computerControlTools = [
|
|
12
|
+
screenshotTool,
|
|
13
|
+
clickTool,
|
|
14
|
+
typeTool,
|
|
15
|
+
keyTool,
|
|
16
|
+
openTool,
|
|
17
|
+
clipboardTool,
|
|
18
|
+
applescriptTool,
|
|
19
|
+
];
|
|
20
|
+
/**
|
|
21
|
+
* `@moxxy/plugin-computer-control` — programmatic control of the host
|
|
22
|
+
* computer (mouse, keyboard, screenshot, clipboard, app launching,
|
|
23
|
+
* AppleScript escape hatch).
|
|
24
|
+
*
|
|
25
|
+
* Currently macOS-only: every tool shells out to built-in binaries
|
|
26
|
+
* (`screencapture`, `osascript`, `open`, `pbpaste`, `pbcopy`). On any
|
|
27
|
+
* other platform the plugin still registers — the tools' handlers
|
|
28
|
+
* throw a clear "macOS only" error — so the model's tool list stays
|
|
29
|
+
* stable across hosts (avoids "tool disappeared on Linux" confusion).
|
|
30
|
+
*
|
|
31
|
+
* Every tool is `permission: 'prompt'`. There is intentionally no
|
|
32
|
+
* "allow always" shortcut for these — granting blanket permission to
|
|
33
|
+
* drive the user's screen + keyboard is exactly the wrong default.
|
|
34
|
+
*/
|
|
35
|
+
export const computerControlPlugin = definePlugin({
|
|
36
|
+
name: '@moxxy/plugin-computer-control',
|
|
37
|
+
version: '0.0.0',
|
|
38
|
+
tools: [...computerControlTools],
|
|
39
|
+
});
|
|
40
|
+
export default computerControlPlugin;
|
|
41
|
+
// Re-export for callers that want a runtime gate.
|
|
42
|
+
export { IS_DARWIN };
|
|
43
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAA6B,MAAM,YAAY,CAAC;AACrE,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AACvC,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AACzD,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC7C,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAC;AACzC,OAAO,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAC3C,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAE3C,OAAO,EACL,eAAe,EACf,SAAS,EACT,aAAa,EACb,OAAO,EACP,QAAQ,EACR,cAAc,EACd,QAAQ,GACT,CAAC;AAEF,MAAM,CAAC,MAAM,oBAAoB,GAA2B;IAC1D,cAAc;IACd,SAAS;IACT,QAAQ;IACR,OAAO;IACP,QAAQ;IACR,aAAa;IACb,eAAe;CAChB,CAAC;AAEF;;;;;;;;;;;;;;GAcG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAAW,YAAY,CAAC;IACxD,IAAI,EAAE,gCAAgC;IACtC,OAAO,EAAE,OAAO;IAChB,KAAK,EAAE,CAAC,GAAG,oBAAoB,CAAC;CACjC,CAAC,CAAC;AAEH,eAAe,qBAAqB,CAAC;AAErC,kDAAkD;AAClD,OAAO,EAAE,SAAS,EAAE,CAAC"}
|
package/dist/shell.d.ts
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
export declare const IS_DARWIN: boolean;
|
|
2
|
+
/**
|
|
3
|
+
* Hard ceiling on combined stdout+stderr a child may emit before it is
|
|
4
|
+
* force-killed. The wall-clock timeout caps duration but not throughput —
|
|
5
|
+
* a runaway or hostile child (e.g. `pbpaste` on a gigabyte clipboard, or an
|
|
6
|
+
* `osascript` snippet that streams unbounded output) can accumulate hundreds
|
|
7
|
+
* of MB well inside the timeout and OOM the host. 16 MB is far above any
|
|
8
|
+
* legitimate tool output here (the screenshot path caps its own encoded file
|
|
9
|
+
* separately) while still bounding worst-case memory.
|
|
10
|
+
*/
|
|
11
|
+
export declare const MAX_OUTPUT_BYTES: number;
|
|
12
|
+
export interface ProcResult {
|
|
13
|
+
readonly exitCode: number;
|
|
14
|
+
readonly stdout: string;
|
|
15
|
+
readonly stderr: string;
|
|
16
|
+
/**
|
|
17
|
+
* True when the child was force-killed because it exceeded `timeoutMs`.
|
|
18
|
+
* A timed-out child still resolves (via 'close') with `exitCode: -1`, so
|
|
19
|
+
* this flag is the only reliable way for callers to distinguish a timeout
|
|
20
|
+
* from a genuine non-zero exit and surface an actionable message.
|
|
21
|
+
*/
|
|
22
|
+
readonly timedOut: boolean;
|
|
23
|
+
/** True when the child was force-killed because `opts.signal` aborted. */
|
|
24
|
+
readonly aborted: boolean;
|
|
25
|
+
/**
|
|
26
|
+
* True when the child was force-killed because its combined stdout+stderr
|
|
27
|
+
* exceeded `MAX_OUTPUT_BYTES`. The captured output is whatever had been
|
|
28
|
+
* read up to the cap.
|
|
29
|
+
*/
|
|
30
|
+
readonly tooLarge: boolean;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Build a uniform failure suffix that names a timeout/abort when the process
|
|
34
|
+
* was force-killed, so a stuck `osascript` (etc.) is reported as a clear
|
|
35
|
+
* cause rather than a bare `exit -1`. Returns '' for a normal exit.
|
|
36
|
+
*/
|
|
37
|
+
export declare function procFailureCause(proc: ProcResult, timeoutMs?: number): string;
|
|
38
|
+
/**
|
|
39
|
+
* Spawn a process with array-form args (no shell). Returns stdout +
|
|
40
|
+
* stderr + exit code. Optional `input` is written to stdin. Optional
|
|
41
|
+
* `signal` propagates aborts from the tool ctx so a stuck `osascript`
|
|
42
|
+
* dies with the turn instead of hanging the parent.
|
|
43
|
+
*
|
|
44
|
+
* Never use this with string interpolation into a single command —
|
|
45
|
+
* each argument MUST be a separate array entry. The `bash -c` shape
|
|
46
|
+
* would re-introduce the shell-injection risk this helper exists to
|
|
47
|
+
* eliminate.
|
|
48
|
+
*/
|
|
49
|
+
export declare function runProcess(cmd: string, args: ReadonlyArray<string>, opts?: {
|
|
50
|
+
readonly input?: string | Buffer;
|
|
51
|
+
readonly signal?: AbortSignal;
|
|
52
|
+
readonly timeoutMs?: number;
|
|
53
|
+
}): Promise<ProcResult>;
|
|
54
|
+
/** Throw a clear error when a tool is invoked on a non-darwin host. */
|
|
55
|
+
export declare function ensureDarwin(toolName: string): void;
|
|
56
|
+
//# sourceMappingURL=shell.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"shell.d.ts","sourceRoot":"","sources":["../src/shell.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,SAAS,SAAgC,CAAC;AAEvD;;;;;;;;GAQG;AACH,eAAO,MAAM,gBAAgB,QAAmB,CAAC;AAKjD,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB;;;;;OAKG;IACH,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;IAC3B,0EAA0E;IAC1E,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B;;;;OAIG;IACH,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;CAC5B;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,UAAU,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,MAAM,CAO7E;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,UAAU,CACxB,GAAG,EAAE,MAAM,EACX,IAAI,EAAE,aAAa,CAAC,MAAM,CAAC,EAC3B,IAAI,GAAE;IACJ,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACjC,QAAQ,CAAC,MAAM,CAAC,EAAE,WAAW,CAAC;IAC9B,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;CACxB,GACL,OAAO,CAAC,UAAU,CAAC,CAgIrB;AAED,uEAAuE;AACvE,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAQnD"}
|
package/dist/shell.js
ADDED
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
import { spawn } from 'node:child_process';
|
|
2
|
+
import { MoxxyError } from '@moxxy/sdk';
|
|
3
|
+
export const IS_DARWIN = process.platform === 'darwin';
|
|
4
|
+
/**
|
|
5
|
+
* Hard ceiling on combined stdout+stderr a child may emit before it is
|
|
6
|
+
* force-killed. The wall-clock timeout caps duration but not throughput —
|
|
7
|
+
* a runaway or hostile child (e.g. `pbpaste` on a gigabyte clipboard, or an
|
|
8
|
+
* `osascript` snippet that streams unbounded output) can accumulate hundreds
|
|
9
|
+
* of MB well inside the timeout and OOM the host. 16 MB is far above any
|
|
10
|
+
* legitimate tool output here (the screenshot path caps its own encoded file
|
|
11
|
+
* separately) while still bounding worst-case memory.
|
|
12
|
+
*/
|
|
13
|
+
export const MAX_OUTPUT_BYTES = 16 * 1024 * 1024;
|
|
14
|
+
/** Grace period after SIGTERM before escalating to SIGKILL. */
|
|
15
|
+
const SIGKILL_GRACE_MS = 2_500;
|
|
16
|
+
/**
|
|
17
|
+
* Build a uniform failure suffix that names a timeout/abort when the process
|
|
18
|
+
* was force-killed, so a stuck `osascript` (etc.) is reported as a clear
|
|
19
|
+
* cause rather than a bare `exit -1`. Returns '' for a normal exit.
|
|
20
|
+
*/
|
|
21
|
+
export function procFailureCause(proc, timeoutMs) {
|
|
22
|
+
if (proc.tooLarge)
|
|
23
|
+
return `output exceeded ${MAX_OUTPUT_BYTES} bytes (killed)`;
|
|
24
|
+
if (proc.timedOut) {
|
|
25
|
+
return timeoutMs ? `timed out after ${timeoutMs}ms` : 'timed out';
|
|
26
|
+
}
|
|
27
|
+
if (proc.aborted)
|
|
28
|
+
return 'aborted (turn cancelled)';
|
|
29
|
+
return '';
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Spawn a process with array-form args (no shell). Returns stdout +
|
|
33
|
+
* stderr + exit code. Optional `input` is written to stdin. Optional
|
|
34
|
+
* `signal` propagates aborts from the tool ctx so a stuck `osascript`
|
|
35
|
+
* dies with the turn instead of hanging the parent.
|
|
36
|
+
*
|
|
37
|
+
* Never use this with string interpolation into a single command —
|
|
38
|
+
* each argument MUST be a separate array entry. The `bash -c` shape
|
|
39
|
+
* would re-introduce the shell-injection risk this helper exists to
|
|
40
|
+
* eliminate.
|
|
41
|
+
*/
|
|
42
|
+
export function runProcess(cmd, args, opts = {}) {
|
|
43
|
+
return new Promise((resolve, reject) => {
|
|
44
|
+
// An ALREADY-aborted signal never emits 'abort' to a listener added after
|
|
45
|
+
// the fact (DOM semantics), so without this short-circuit a cancelled turn
|
|
46
|
+
// would still spawn the child and run it to completion — defeating the
|
|
47
|
+
// abort contract. Resolve immediately as an aborted no-op without spawning.
|
|
48
|
+
if (opts.signal?.aborted) {
|
|
49
|
+
resolve({
|
|
50
|
+
exitCode: -1,
|
|
51
|
+
stdout: '',
|
|
52
|
+
stderr: '',
|
|
53
|
+
timedOut: false,
|
|
54
|
+
aborted: true,
|
|
55
|
+
tooLarge: false,
|
|
56
|
+
});
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
const child = spawn(cmd, [...args], { stdio: ['pipe', 'pipe', 'pipe'] });
|
|
60
|
+
// Collect BOTH stdout and stderr as chunks and concat ONCE at close.
|
|
61
|
+
// Re-concatenating the whole accumulated buffer on every 'data' event is
|
|
62
|
+
// O(n^2) and churns the GC; decoding each chunk with `.toString('utf8')`
|
|
63
|
+
// in isolation also corrupts any multibyte sequence (e.g. an osascript
|
|
64
|
+
// error containing a non-ASCII app name) that straddles a chunk boundary.
|
|
65
|
+
const stdoutChunks = [];
|
|
66
|
+
const stderrChunks = [];
|
|
67
|
+
let outputBytes = 0;
|
|
68
|
+
let settled = false;
|
|
69
|
+
let timedOut = false;
|
|
70
|
+
let aborted = false;
|
|
71
|
+
let tooLarge = false;
|
|
72
|
+
let killTimer = null;
|
|
73
|
+
// Force the child to die: SIGTERM first, then escalate to SIGKILL after a
|
|
74
|
+
// grace period for a child that traps/ignores SIGTERM (a wedged GUI helper
|
|
75
|
+
// or an osascript stuck behind an Accessibility prompt). Without the
|
|
76
|
+
// escalation the tool's timeout/abort isn't actually enforced — the parent
|
|
77
|
+
// hangs until the child closes on its own, which may be never.
|
|
78
|
+
const forceKill = () => {
|
|
79
|
+
try {
|
|
80
|
+
child.kill('SIGTERM');
|
|
81
|
+
}
|
|
82
|
+
catch {
|
|
83
|
+
/* ignore */
|
|
84
|
+
}
|
|
85
|
+
if (killTimer)
|
|
86
|
+
return;
|
|
87
|
+
killTimer = setTimeout(() => {
|
|
88
|
+
if (settled)
|
|
89
|
+
return;
|
|
90
|
+
try {
|
|
91
|
+
child.kill('SIGKILL');
|
|
92
|
+
}
|
|
93
|
+
catch {
|
|
94
|
+
/* ignore */
|
|
95
|
+
}
|
|
96
|
+
}, SIGKILL_GRACE_MS);
|
|
97
|
+
killTimer.unref?.();
|
|
98
|
+
};
|
|
99
|
+
const onAbort = () => {
|
|
100
|
+
if (settled)
|
|
101
|
+
return;
|
|
102
|
+
aborted = true;
|
|
103
|
+
forceKill();
|
|
104
|
+
};
|
|
105
|
+
opts.signal?.addEventListener('abort', onAbort, { once: true });
|
|
106
|
+
const timer = opts.timeoutMs
|
|
107
|
+
? setTimeout(() => {
|
|
108
|
+
if (settled)
|
|
109
|
+
return;
|
|
110
|
+
timedOut = true;
|
|
111
|
+
forceKill();
|
|
112
|
+
}, opts.timeoutMs)
|
|
113
|
+
: null;
|
|
114
|
+
child.stdout.on('data', (chunk) => {
|
|
115
|
+
outputBytes += chunk.length;
|
|
116
|
+
// Once over the cap we stop retaining further chunks (the child is being
|
|
117
|
+
// killed) so a flood between the cap-trip and SIGTERM landing can't keep
|
|
118
|
+
// growing memory; we still account the bytes to keep `outputBytes` honest.
|
|
119
|
+
if (!tooLarge)
|
|
120
|
+
stdoutChunks.push(chunk);
|
|
121
|
+
if (!tooLarge && outputBytes > MAX_OUTPUT_BYTES) {
|
|
122
|
+
tooLarge = true;
|
|
123
|
+
forceKill();
|
|
124
|
+
}
|
|
125
|
+
});
|
|
126
|
+
child.stderr.on('data', (chunk) => {
|
|
127
|
+
outputBytes += chunk.length;
|
|
128
|
+
if (!tooLarge)
|
|
129
|
+
stderrChunks.push(chunk);
|
|
130
|
+
if (!tooLarge && outputBytes > MAX_OUTPUT_BYTES) {
|
|
131
|
+
tooLarge = true;
|
|
132
|
+
forceKill();
|
|
133
|
+
}
|
|
134
|
+
});
|
|
135
|
+
// A child that exits/closes stdin before our write completes makes the
|
|
136
|
+
// stdin Writable emit 'error' (EPIPE/ECONNRESET). With no listener that
|
|
137
|
+
// throws and surfaces as an unhandled rejection that can take down the
|
|
138
|
+
// parent — swallow it; the child 'error'/'close' path reports the real
|
|
139
|
+
// failure. Same defensiveness on stdout/stderr for symmetry.
|
|
140
|
+
child.stdin.on('error', () => { });
|
|
141
|
+
child.stdout.on('error', () => { });
|
|
142
|
+
child.stderr.on('error', () => { });
|
|
143
|
+
const cleanup = () => {
|
|
144
|
+
if (timer)
|
|
145
|
+
clearTimeout(timer);
|
|
146
|
+
if (killTimer)
|
|
147
|
+
clearTimeout(killTimer);
|
|
148
|
+
opts.signal?.removeEventListener('abort', onAbort);
|
|
149
|
+
};
|
|
150
|
+
child.once('error', (err) => {
|
|
151
|
+
if (settled)
|
|
152
|
+
return;
|
|
153
|
+
settled = true;
|
|
154
|
+
cleanup();
|
|
155
|
+
reject(err);
|
|
156
|
+
});
|
|
157
|
+
child.once('close', (code) => {
|
|
158
|
+
if (settled)
|
|
159
|
+
return;
|
|
160
|
+
settled = true;
|
|
161
|
+
cleanup();
|
|
162
|
+
resolve({
|
|
163
|
+
exitCode: code ?? -1,
|
|
164
|
+
stdout: Buffer.concat(stdoutChunks).toString('utf8'),
|
|
165
|
+
stderr: Buffer.concat(stderrChunks).toString('utf8'),
|
|
166
|
+
timedOut,
|
|
167
|
+
aborted,
|
|
168
|
+
tooLarge,
|
|
169
|
+
});
|
|
170
|
+
});
|
|
171
|
+
if (opts.input !== undefined) {
|
|
172
|
+
child.stdin.end(opts.input);
|
|
173
|
+
}
|
|
174
|
+
else {
|
|
175
|
+
child.stdin.end();
|
|
176
|
+
}
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
/** Throw a clear error when a tool is invoked on a non-darwin host. */
|
|
180
|
+
export function ensureDarwin(toolName) {
|
|
181
|
+
if (!IS_DARWIN) {
|
|
182
|
+
throw new MoxxyError({
|
|
183
|
+
code: 'TOOL_ERROR',
|
|
184
|
+
message: `${toolName}: @moxxy/plugin-computer-control currently only supports macOS (process.platform = ${process.platform})`,
|
|
185
|
+
context: { tool: toolName, platform: process.platform },
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
//# sourceMappingURL=shell.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"shell.js","sourceRoot":"","sources":["../src/shell.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"applescript.d.ts","sourceRoot":"","sources":["../../src/tools/applescript.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,eAAe,8BAqC1B,CAAC"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { defineTool, MoxxyError, z } from '@moxxy/sdk';
|
|
2
|
+
import { ensureDarwin, procFailureCause, runProcess } from '../shell.js';
|
|
3
|
+
export const applescriptTool = defineTool({
|
|
4
|
+
name: 'computer_applescript',
|
|
5
|
+
description: 'Escape hatch: run an arbitrary AppleScript snippet via `osascript`. Use ' +
|
|
6
|
+
'for anything the dedicated tools can\'t cover (frontmost app name, list ' +
|
|
7
|
+
'open Safari tabs, drive a specific app via its scripting dictionary, etc.). ' +
|
|
8
|
+
'Returns the script\'s stdout. The user approves every invocation — keep ' +
|
|
9
|
+
'scripts focused and reversible.',
|
|
10
|
+
inputSchema: z.object({
|
|
11
|
+
script: z
|
|
12
|
+
.string()
|
|
13
|
+
.min(1)
|
|
14
|
+
.max(8000)
|
|
15
|
+
.describe('AppleScript source. Multi-line is fine — pass `\\n` between statements. ' +
|
|
16
|
+
'For JavaScript for Automation, prefix with `#!/usr/bin/osascript -l JavaScript`'),
|
|
17
|
+
}),
|
|
18
|
+
permission: { action: 'prompt' },
|
|
19
|
+
async handler({ script }, ctx) {
|
|
20
|
+
ensureDarwin('computer_applescript');
|
|
21
|
+
const proc = await runProcess('osascript', ['-e', script], {
|
|
22
|
+
...(ctx.signal ? { signal: ctx.signal } : {}),
|
|
23
|
+
timeoutMs: 30_000,
|
|
24
|
+
});
|
|
25
|
+
if (proc.exitCode !== 0) {
|
|
26
|
+
const cause = procFailureCause(proc, 30_000);
|
|
27
|
+
throw new MoxxyError({
|
|
28
|
+
code: 'TOOL_ERROR',
|
|
29
|
+
message: cause
|
|
30
|
+
? `osascript ${cause}`
|
|
31
|
+
: `osascript failed (exit ${proc.exitCode}): ${proc.stderr.trim() || '(no error message)'}`,
|
|
32
|
+
context: { tool: 'computer_applescript', exitCode: proc.exitCode, timedOut: proc.timedOut ? 1 : 0 },
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
return { ok: true, output: proc.stdout.trim() };
|
|
36
|
+
},
|
|
37
|
+
});
|
|
38
|
+
//# sourceMappingURL=applescript.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"applescript.js","sourceRoot":"","sources":["../../src/tools/applescript.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,CAAC,EAAE,MAAM,YAAY,CAAC;AACvD,OAAO,EAAE,YAAY,EAAE,gBAAgB,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAEzE,MAAM,CAAC,MAAM,eAAe,GAAG,UAAU,CAAC;IACxC,IAAI,EAAE,sBAAsB;IAC5B,WAAW,EACT,0EAA0E;QAC1E,0EAA0E;QAC1E,8EAA8E;QAC9E,0EAA0E;QAC1E,iCAAiC;IACnC,WAAW,EAAE,CAAC,CAAC,MAAM,CAAC;QACpB,MAAM,EAAE,CAAC;aACN,MAAM,EAAE;aACR,GAAG,CAAC,CAAC,CAAC;aACN,GAAG,CAAC,IAAI,CAAC;aACT,QAAQ,CACP,0EAA0E;YACxE,iFAAiF,CACpF;KACJ,CAAC;IACF,UAAU,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE;IAChC,KAAK,CAAC,OAAO,CAAC,EAAE,MAAM,EAAE,EAAE,GAAG;QAC3B,YAAY,CAAC,sBAAsB,CAAC,CAAC;QACrC,MAAM,IAAI,GAAG,MAAM,UAAU,CAAC,WAAW,EAAE,CAAC,IAAI,EAAE,MAAM,CAAC,EAAE;YACzD,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YAC7C,SAAS,EAAE,MAAM;SAClB,CAAC,CAAC;QACH,IAAI,IAAI,CAAC,QAAQ,KAAK,CAAC,EAAE,CAAC;YACxB,MAAM,KAAK,GAAG,gBAAgB,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;YAC7C,MAAM,IAAI,UAAU,CAAC;gBACnB,IAAI,EAAE,YAAY;gBAClB,OAAO,EAAE,KAAK;oBACZ,CAAC,CAAC,aAAa,KAAK,EAAE;oBACtB,CAAC,CAAC,0BAA0B,IAAI,CAAC,QAAQ,MAAM,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,IAAI,oBAAoB,EAAE;gBAC7F,OAAO,EAAE,EAAE,IAAI,EAAE,sBAAsB,EAAE,QAAQ,EAAE,IAAI,CAAC,QAAQ,EAAE,QAAQ,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE;aACpG,CAAC,CAAC;QACL,CAAC;QACD,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,EAAE,CAAC;IAClD,CAAC;CACF,CAAC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"click.d.ts","sourceRoot":"","sources":["../../src/tools/click.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,SAAS,8BAgDpB,CAAC"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { defineTool, MoxxyError, z } from '@moxxy/sdk';
|
|
2
|
+
import { ensureDarwin, procFailureCause, runProcess } from '../shell.js';
|
|
3
|
+
export const clickTool = defineTool({
|
|
4
|
+
name: 'computer_click',
|
|
5
|
+
description: 'Click the mouse at screen-pixel coordinates (top-left origin). Uses macOS ' +
|
|
6
|
+
'System Events; requires Accessibility permission on first use. Pass count=2 ' +
|
|
7
|
+
'for a double-click. Right/middle buttons are not supported via this tool — ' +
|
|
8
|
+
'use computer_applescript for those (rare).',
|
|
9
|
+
inputSchema: z.object({
|
|
10
|
+
x: z.number().int().min(0).describe('X pixel from the top-left of the display.'),
|
|
11
|
+
y: z.number().int().min(0).describe('Y pixel from the top-left of the display.'),
|
|
12
|
+
count: z
|
|
13
|
+
.number()
|
|
14
|
+
.int()
|
|
15
|
+
.min(1)
|
|
16
|
+
.max(3)
|
|
17
|
+
.optional()
|
|
18
|
+
.describe('Number of consecutive clicks. 1 = single, 2 = double, 3 = triple. Default 1.'),
|
|
19
|
+
}),
|
|
20
|
+
permission: { action: 'prompt' },
|
|
21
|
+
async handler({ x, y, count }, ctx) {
|
|
22
|
+
ensureDarwin('computer_click');
|
|
23
|
+
const n = count ?? 1;
|
|
24
|
+
// `click at {x, y}` repeats N times via a repeat block. Use a
|
|
25
|
+
// bare AppleScript string with numeric interpolation — no
|
|
26
|
+
// user-supplied text reaches the script, so injection isn't a
|
|
27
|
+
// risk here.
|
|
28
|
+
const script = `tell application "System Events"\n` +
|
|
29
|
+
` repeat ${n} times\n` +
|
|
30
|
+
` click at {${Math.round(x)}, ${Math.round(y)}}\n` +
|
|
31
|
+
` end repeat\n` +
|
|
32
|
+
`end tell`;
|
|
33
|
+
const proc = await runProcess('osascript', ['-e', script], {
|
|
34
|
+
...(ctx.signal ? { signal: ctx.signal } : {}),
|
|
35
|
+
timeoutMs: 10_000,
|
|
36
|
+
});
|
|
37
|
+
if (proc.exitCode !== 0) {
|
|
38
|
+
const cause = procFailureCause(proc, 10_000);
|
|
39
|
+
throw new MoxxyError({
|
|
40
|
+
code: 'TOOL_ERROR',
|
|
41
|
+
message: cause
|
|
42
|
+
? `click ${cause}`
|
|
43
|
+
: `click failed (exit ${proc.exitCode}): ${proc.stderr.trim() || '(check Accessibility permission in System Settings → Privacy & Security → Accessibility)'}`,
|
|
44
|
+
context: { tool: 'computer_click', exitCode: proc.exitCode, timedOut: proc.timedOut ? 1 : 0 },
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
return { ok: true, x, y, count: n };
|
|
48
|
+
},
|
|
49
|
+
});
|
|
50
|
+
//# sourceMappingURL=click.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"click.js","sourceRoot":"","sources":["../../src/tools/click.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,CAAC,EAAE,MAAM,YAAY,CAAC;AACvD,OAAO,EAAE,YAAY,EAAE,gBAAgB,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAEzE,MAAM,CAAC,MAAM,SAAS,GAAG,UAAU,CAAC;IAClC,IAAI,EAAE,gBAAgB;IACtB,WAAW,EACT,4EAA4E;QAC5E,8EAA8E;QAC9E,6EAA6E;QAC7E,4CAA4C;IAC9C,WAAW,EAAE,CAAC,CAAC,MAAM,CAAC;QACpB,CAAC,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,2CAA2C,CAAC;QAChF,CAAC,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,2CAA2C,CAAC;QAChF,KAAK,EAAE,CAAC;aACL,MAAM,EAAE;aACR,GAAG,EAAE;aACL,GAAG,CAAC,CAAC,CAAC;aACN,GAAG,CAAC,CAAC,CAAC;aACN,QAAQ,EAAE;aACV,QAAQ,CAAC,8EAA8E,CAAC;KAC5F,CAAC;IACF,UAAU,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE;IAChC,KAAK,CAAC,OAAO,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,KAAK,EAAE,EAAE,GAAG;QAChC,YAAY,CAAC,gBAAgB,CAAC,CAAC;QAC/B,MAAM,CAAC,GAAG,KAAK,IAAI,CAAC,CAAC;QACrB,8DAA8D;QAC9D,0DAA0D;QAC1D,8DAA8D;QAC9D,aAAa;QACb,MAAM,MAAM,GACV,oCAAoC;YACpC,YAAY,CAAC,UAAU;YACvB,iBAAiB,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK;YACrD,gBAAgB;YAChB,UAAU,CAAC;QACb,MAAM,IAAI,GAAG,MAAM,UAAU,CAAC,WAAW,EAAE,CAAC,IAAI,EAAE,MAAM,CAAC,EAAE;YACzD,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YAC7C,SAAS,EAAE,MAAM;SAClB,CAAC,CAAC;QACH,IAAI,IAAI,CAAC,QAAQ,KAAK,CAAC,EAAE,CAAC;YACxB,MAAM,KAAK,GAAG,gBAAgB,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;YAC7C,MAAM,IAAI,UAAU,CAAC;gBACnB,IAAI,EAAE,YAAY;gBAClB,OAAO,EAAE,KAAK;oBACZ,CAAC,CAAC,SAAS,KAAK,EAAE;oBAClB,CAAC,CAAC,sBAAsB,IAAI,CAAC,QAAQ,MAAM,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,IAAI,0FAA0F,EAAE;gBAC/J,OAAO,EAAE,EAAE,IAAI,EAAE,gBAAgB,EAAE,QAAQ,EAAE,IAAI,CAAC,QAAQ,EAAE,QAAQ,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE;aAC9F,CAAC,CAAC;QACL,CAAC;QACD,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC;IACtC,CAAC;CACF,CAAC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"clipboard.d.ts","sourceRoot":"","sources":["../../src/tools/clipboard.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,aAAa,8BA2DxB,CAAC"}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { defineTool, MoxxyError, z } from '@moxxy/sdk';
|
|
2
|
+
import { ensureDarwin, procFailureCause, runProcess } from '../shell.js';
|
|
3
|
+
export const clipboardTool = defineTool({
|
|
4
|
+
name: 'computer_clipboard',
|
|
5
|
+
description: 'Read from or write to the macOS clipboard. Use `read` to fetch what the ' +
|
|
6
|
+
'user just copied; use `write` to stage text the user can then paste with ' +
|
|
7
|
+
'⌘V. Writing the clipboard does NOT trigger a paste — call computer_key ' +
|
|
8
|
+
'with cmd+v after if you need to paste.',
|
|
9
|
+
inputSchema: z.object({
|
|
10
|
+
action: z.enum(['read', 'write']),
|
|
11
|
+
text: z
|
|
12
|
+
.string()
|
|
13
|
+
.max(64_000)
|
|
14
|
+
.optional()
|
|
15
|
+
.describe('Text to put on the clipboard. Required when action="write".'),
|
|
16
|
+
}),
|
|
17
|
+
permission: { action: 'prompt' },
|
|
18
|
+
async handler({ action, text }, ctx) {
|
|
19
|
+
ensureDarwin('computer_clipboard');
|
|
20
|
+
if (action === 'read') {
|
|
21
|
+
const proc = await runProcess('pbpaste', [], {
|
|
22
|
+
...(ctx.signal ? { signal: ctx.signal } : {}),
|
|
23
|
+
timeoutMs: 5_000,
|
|
24
|
+
});
|
|
25
|
+
if (proc.exitCode !== 0) {
|
|
26
|
+
const cause = procFailureCause(proc, 5_000);
|
|
27
|
+
throw new MoxxyError({
|
|
28
|
+
code: 'TOOL_ERROR',
|
|
29
|
+
message: cause
|
|
30
|
+
? `pbpaste ${cause}`
|
|
31
|
+
: `pbpaste failed (exit ${proc.exitCode}): ${proc.stderr.trim()}`,
|
|
32
|
+
context: { tool: 'computer_clipboard', exitCode: proc.exitCode, timedOut: proc.timedOut ? 1 : 0 },
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
return { ok: true, text: proc.stdout };
|
|
36
|
+
}
|
|
37
|
+
if (text === undefined) {
|
|
38
|
+
throw new MoxxyError({
|
|
39
|
+
code: 'TOOL_ERROR',
|
|
40
|
+
message: 'computer_clipboard: `text` is required when action="write"',
|
|
41
|
+
context: { tool: 'computer_clipboard' },
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
const proc = await runProcess('pbcopy', [], {
|
|
45
|
+
...(ctx.signal ? { signal: ctx.signal } : {}),
|
|
46
|
+
input: text,
|
|
47
|
+
timeoutMs: 5_000,
|
|
48
|
+
});
|
|
49
|
+
if (proc.exitCode !== 0) {
|
|
50
|
+
const cause = procFailureCause(proc, 5_000);
|
|
51
|
+
throw new MoxxyError({
|
|
52
|
+
code: 'TOOL_ERROR',
|
|
53
|
+
message: cause
|
|
54
|
+
? `pbcopy ${cause}`
|
|
55
|
+
: `pbcopy failed (exit ${proc.exitCode}): ${proc.stderr.trim()}`,
|
|
56
|
+
context: { tool: 'computer_clipboard', exitCode: proc.exitCode, timedOut: proc.timedOut ? 1 : 0 },
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
return { ok: true, length: text.length };
|
|
60
|
+
},
|
|
61
|
+
});
|
|
62
|
+
//# sourceMappingURL=clipboard.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"clipboard.js","sourceRoot":"","sources":["../../src/tools/clipboard.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
declare const MODIFIER_NAMES: readonly ["cmd", "shift", "option", "control"];
|
|
2
|
+
type Modifier = (typeof MODIFIER_NAMES)[number];
|
|
3
|
+
export declare const keyTool: import("@moxxy/sdk").ToolDef;
|
|
4
|
+
/**
|
|
5
|
+
* Build the `osascript -e` body for a single key chord. Named keys go
|
|
6
|
+
* through `key code N`, single characters through `keystroke "x"`, and a
|
|
7
|
+
* multi-char unknown key throws. Pure (no I/O) so it can be unit-tested.
|
|
8
|
+
*/
|
|
9
|
+
export declare function buildKeyScript(key: string, modifiers: ReadonlyArray<Modifier>): string;
|
|
10
|
+
export {};
|
|
11
|
+
//# sourceMappingURL=key.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"key.d.ts","sourceRoot":"","sources":["../../src/tools/key.ts"],"names":[],"mappings":"AAGA,QAAA,MAAM,cAAc,gDAAiD,CAAC;AACtE,KAAK,QAAQ,GAAG,CAAC,OAAO,cAAc,CAAC,CAAC,MAAM,CAAC,CAAC;AAoDhD,eAAO,MAAM,OAAO,8BA2ClB,CAAC;AAEH;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,EAAE,SAAS,EAAE,aAAa,CAAC,QAAQ,CAAC,GAAG,MAAM,CAyBtF"}
|