usecomputer 0.0.1 → 0.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +31 -0
- package/README.md +51 -0
- package/bin.js +3 -1
- package/build.zig +53 -0
- package/build.zig.zon +21 -0
- package/dist/bridge-contract.test.d.ts +2 -0
- package/dist/bridge-contract.test.d.ts.map +1 -0
- package/dist/bridge-contract.test.js +74 -0
- package/dist/bridge.d.ts +7 -0
- package/dist/bridge.d.ts.map +1 -0
- package/dist/bridge.js +130 -0
- package/dist/cli-parsing.test.d.ts +2 -0
- package/dist/cli-parsing.test.d.ts.map +1 -0
- package/dist/cli-parsing.test.js +30 -0
- package/dist/cli.d.ts +5 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +297 -335
- package/dist/command-parsers.d.ts +6 -0
- package/dist/command-parsers.d.ts.map +1 -0
- package/dist/command-parsers.js +54 -0
- package/dist/command-parsers.test.d.ts +2 -0
- package/dist/command-parsers.test.d.ts.map +1 -0
- package/dist/command-parsers.test.js +44 -0
- package/dist/darwin-arm64/usecomputer.node +0 -0
- package/dist/darwin-x64/usecomputer.node +0 -0
- package/dist/index.d.ts +4 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -4
- package/dist/native-click-smoke.test.d.ts +2 -0
- package/dist/native-click-smoke.test.d.ts.map +1 -0
- package/dist/native-click-smoke.test.js +93 -0
- package/dist/native-lib.cjs +33 -0
- package/dist/native-lib.d.cts +7 -0
- package/dist/native-lib.d.ts +5 -0
- package/dist/native-lib.d.ts.map +1 -0
- package/dist/native-lib.js +27 -0
- package/dist/types.d.ts +80 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/package.json +23 -12
- package/src/bridge-contract.test.ts +85 -0
- package/src/bridge.ts +159 -0
- package/src/cli.ts +344 -473
- package/src/command-parsers.test.ts +50 -0
- package/src/command-parsers.ts +60 -0
- package/src/index.ts +5 -4
- package/src/native-click-smoke.test.ts +131 -0
- package/src/native-lib.ts +38 -0
- package/src/types.ts +87 -0
- package/zig/src/lib.zig +493 -0
package/dist/cli.js
CHANGED
|
@@ -1,339 +1,301 @@
|
|
|
1
|
-
// usecomputer CLI
|
|
2
|
-
// Outline only. Commands print "not implemented" placeholders.
|
|
1
|
+
// usecomputer CLI entrypoint and command wiring for desktop automation actions.
|
|
3
2
|
import { goke } from 'goke';
|
|
4
3
|
import { z } from 'zod';
|
|
5
4
|
import dedent from 'string-dedent';
|
|
6
|
-
import
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
5
|
+
import { createRequire } from 'node:module';
|
|
6
|
+
import fs from 'node:fs';
|
|
7
|
+
import pathModule from 'node:path';
|
|
8
|
+
import url from 'node:url';
|
|
9
|
+
import { createBridge } from './bridge.js';
|
|
10
|
+
import { parseDirection, parseModifiers, parsePoint, parseRegion } from './command-parsers.js';
|
|
11
|
+
const require = createRequire(import.meta.url);
|
|
12
|
+
const packageJson = require('../package.json');
|
|
13
|
+
function printJson(value) {
|
|
14
|
+
process.stdout.write(`${JSON.stringify(value, null, 2)}\n`);
|
|
15
|
+
}
|
|
16
|
+
function printLine(value) {
|
|
17
|
+
process.stdout.write(`${value}\n`);
|
|
18
|
+
}
|
|
19
|
+
function parsePointOrThrow(input) {
|
|
20
|
+
const parsed = parsePoint(input);
|
|
21
|
+
if (parsed instanceof Error) {
|
|
22
|
+
throw parsed;
|
|
23
|
+
}
|
|
24
|
+
return parsed;
|
|
25
|
+
}
|
|
26
|
+
function resolvePointInput({ x, y, target, command, }) {
|
|
27
|
+
if (typeof x === 'number' || typeof y === 'number') {
|
|
28
|
+
if (typeof x !== 'number' || typeof y !== 'number') {
|
|
29
|
+
throw new Error(`Command \"${command}\" requires both -x and -y when using coordinate flags`);
|
|
30
|
+
}
|
|
31
|
+
return { x, y };
|
|
32
|
+
}
|
|
33
|
+
if (target) {
|
|
34
|
+
return parsePointOrThrow(target);
|
|
35
|
+
}
|
|
36
|
+
throw new Error(`Command \"${command}\" requires coordinates. Use -x <n> -y <n>`);
|
|
37
|
+
}
|
|
38
|
+
function parseButton(input) {
|
|
39
|
+
if (input === 'right' || input === 'middle') {
|
|
40
|
+
return input;
|
|
41
|
+
}
|
|
42
|
+
return 'left';
|
|
43
|
+
}
|
|
44
|
+
function notImplemented({ command }) {
|
|
45
|
+
throw new Error(`TODO not implemented: ${command}`);
|
|
46
|
+
}
|
|
47
|
+
export function createCli({ bridge = createBridge() } = {}) {
|
|
48
|
+
const cli = goke('usecomputer');
|
|
49
|
+
cli
|
|
50
|
+
.command('screenshot [path]', dedent `
|
|
51
|
+
Take a screenshot of the entire screen or a region.
|
|
12
52
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
});
|
|
72
|
-
cli
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
});
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
});
|
|
136
|
-
cli
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
});
|
|
147
|
-
|
|
148
|
-
cli
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
});
|
|
159
|
-
cli
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
});
|
|
186
|
-
|
|
187
|
-
.command('get
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
.action(() => {
|
|
194
|
-
|
|
195
|
-
});
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
.
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
});
|
|
204
|
-
|
|
205
|
-
.command('window focus <target>'
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
.
|
|
212
|
-
|
|
213
|
-
});
|
|
214
|
-
cli
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
});
|
|
219
|
-
|
|
220
|
-
.command('window
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
.action((
|
|
227
|
-
|
|
228
|
-
});
|
|
229
|
-
cli
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
});
|
|
234
|
-
|
|
235
|
-
cli
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
.action((
|
|
239
|
-
|
|
240
|
-
});
|
|
241
|
-
cli
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
.
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
});
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
.action(() => {
|
|
263
|
-
console.log('not implemented');
|
|
264
|
-
});
|
|
265
|
-
cli
|
|
266
|
-
.command('clipboard set <text>', 'Set the clipboard content to the given text.')
|
|
267
|
-
.action((text) => {
|
|
268
|
-
console.log('not implemented');
|
|
269
|
-
});
|
|
270
|
-
// ─── Wait ───────────────────────────────────────────────────────────────
|
|
271
|
-
cli
|
|
272
|
-
.command('wait <target>', dedent `
|
|
273
|
-
Wait for a condition before continuing.
|
|
274
|
-
|
|
275
|
-
Target can be:
|
|
276
|
-
- Milliseconds: "2000" (wait 2 seconds)
|
|
277
|
-
- Accessibility ref: "@e5" (wait for element to appear)
|
|
278
|
-
- Window title: "--window Untitled" (wait for window to appear)
|
|
279
|
-
`)
|
|
280
|
-
.option('-w, --window [window]', z.string().describe('Wait for a window with this title to appear'))
|
|
281
|
-
.option('--timeout [timeout]', z.number().default(30000).describe('Maximum wait time in milliseconds'))
|
|
282
|
-
.example('# Wait 2 seconds')
|
|
283
|
-
.example('usecomputer wait 2000')
|
|
284
|
-
.example('# Wait for an element to appear')
|
|
285
|
-
.example('usecomputer wait @e5')
|
|
286
|
-
.example('# Wait for a window to appear')
|
|
287
|
-
.example('usecomputer wait --window "Save As"')
|
|
288
|
-
.action((target, options) => {
|
|
289
|
-
console.log('not implemented');
|
|
290
|
-
});
|
|
291
|
-
// ─── Display ────────────────────────────────────────────────────────────
|
|
292
|
-
cli
|
|
293
|
-
.command('display list', 'List connected displays with their resolutions, positions, and scale factors.')
|
|
294
|
-
.option('--json', 'Output as JSON')
|
|
295
|
-
.action((options) => {
|
|
296
|
-
console.log('not implemented');
|
|
297
|
-
});
|
|
298
|
-
// ─── Find Elements ──────────────────────────────────────────────────────
|
|
299
|
-
cli
|
|
300
|
-
.command('find <query>', dedent `
|
|
301
|
-
Search for UI elements matching a text query across the accessibility tree.
|
|
302
|
-
|
|
303
|
-
Returns matching elements with their refs, roles, and positions.
|
|
304
|
-
Useful for locating elements before clicking or typing.
|
|
305
|
-
`)
|
|
306
|
-
.option('-w, --window [window]', z.string().describe('Scope search to a specific window'))
|
|
307
|
-
.option('-a, --app [app]', z.string().describe('Scope search to a specific application'))
|
|
308
|
-
.option('--role [role]', z.string().describe('Filter by accessibility role (button, textField, link, etc.)'))
|
|
309
|
-
.option('--limit [limit]', z.number().default(20).describe('Maximum number of results'))
|
|
310
|
-
.example('# Find all buttons with "Save" in the name')
|
|
311
|
-
.example('usecomputer find "Save" --role button')
|
|
312
|
-
.example('# Find elements in a specific app')
|
|
313
|
-
.example('usecomputer find "File" --app "Visual Studio Code"')
|
|
314
|
-
.action((query, options) => {
|
|
315
|
-
console.log('not implemented');
|
|
316
|
-
});
|
|
317
|
-
// ─── Diff ───────────────────────────────────────────────────────────────
|
|
318
|
-
cli
|
|
319
|
-
.command('diff snapshot', 'Compare the current accessibility snapshot against the previous one. Shows added, removed, and changed elements.')
|
|
320
|
-
.option('-w, --window [window]', z.string().describe('Scope to a specific window'))
|
|
321
|
-
.option('-a, --app [app]', z.string().describe('Scope to a specific application'))
|
|
322
|
-
.action((options) => {
|
|
323
|
-
console.log('not implemented');
|
|
324
|
-
});
|
|
325
|
-
cli
|
|
326
|
-
.command('diff screenshot', 'Compare the current screenshot against a baseline image. Highlights visual differences.')
|
|
327
|
-
.option('--baseline <baseline>', z.string().describe('Path to the baseline screenshot'))
|
|
328
|
-
.option('--threshold [threshold]', z.number().default(0.1).describe('Pixel difference threshold (0-1)'))
|
|
329
|
-
.action((options) => {
|
|
330
|
-
console.log('not implemented');
|
|
331
|
-
});
|
|
332
|
-
// ─── Global Options ─────────────────────────────────────────────────────
|
|
333
|
-
cli.option('--json', 'Output as JSON');
|
|
334
|
-
cli.option('--display [display]', z.number().describe('Target display/monitor index for multi-monitor setups'));
|
|
335
|
-
cli.option('--timeout [timeout]', z.number().default(25000).describe('Default timeout for operations in milliseconds'));
|
|
336
|
-
cli.option('--debug', 'Enable debug output');
|
|
337
|
-
cli.help();
|
|
338
|
-
cli.version(pkg.version);
|
|
339
|
-
cli.parse();
|
|
53
|
+
This command uses a native Zig backend over macOS APIs.
|
|
54
|
+
`)
|
|
55
|
+
.option('-r, --region [region]', z.string().describe('Capture region as x,y,width,height'))
|
|
56
|
+
.option('--display [display]', z.number().describe('Display index for multi-monitor setups (0-based: first display is index 0)'))
|
|
57
|
+
.option('--annotate', 'Annotate screenshot with labels')
|
|
58
|
+
.option('--json', 'Output as JSON')
|
|
59
|
+
.action(async (path, options) => {
|
|
60
|
+
const outputPath = path
|
|
61
|
+
? path.startsWith('/')
|
|
62
|
+
? path
|
|
63
|
+
: `${process.cwd()}/${path}`
|
|
64
|
+
: undefined;
|
|
65
|
+
if (path) {
|
|
66
|
+
const parentDirectory = pathModule.dirname(outputPath);
|
|
67
|
+
fs.mkdirSync(parentDirectory, { recursive: true });
|
|
68
|
+
}
|
|
69
|
+
const region = options.region ? parseRegion(options.region) : undefined;
|
|
70
|
+
if (region instanceof Error) {
|
|
71
|
+
throw region;
|
|
72
|
+
}
|
|
73
|
+
const result = await bridge.screenshot({
|
|
74
|
+
path: outputPath,
|
|
75
|
+
region,
|
|
76
|
+
display: options.display,
|
|
77
|
+
annotate: options.annotate,
|
|
78
|
+
});
|
|
79
|
+
if (options.json) {
|
|
80
|
+
printJson(result);
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
printLine(result.path);
|
|
84
|
+
});
|
|
85
|
+
cli
|
|
86
|
+
.command('click [target]', 'Click at coordinates')
|
|
87
|
+
.option('-x [x]', z.number().describe('X coordinate'))
|
|
88
|
+
.option('-y [y]', z.number().describe('Y coordinate'))
|
|
89
|
+
.option('--button [button]', z.enum(['left', 'right', 'middle']).default('left').describe('Mouse button'))
|
|
90
|
+
.option('--count [count]', z.number().default(1).describe('Number of clicks'))
|
|
91
|
+
.option('--modifiers [modifiers]', z.string().describe('Modifiers as ctrl,shift,alt,meta'))
|
|
92
|
+
.action(async (target, options) => {
|
|
93
|
+
const point = resolvePointInput({
|
|
94
|
+
x: options.x,
|
|
95
|
+
y: options.y,
|
|
96
|
+
target,
|
|
97
|
+
command: 'click',
|
|
98
|
+
});
|
|
99
|
+
await bridge.click({
|
|
100
|
+
point,
|
|
101
|
+
button: options.button,
|
|
102
|
+
count: options.count,
|
|
103
|
+
modifiers: parseModifiers(options.modifiers),
|
|
104
|
+
});
|
|
105
|
+
});
|
|
106
|
+
cli
|
|
107
|
+
.command('type <text>', 'Type text in the focused element')
|
|
108
|
+
.option('--delay [delay]', z.number().describe('Delay in milliseconds between keystrokes'))
|
|
109
|
+
.action(async (text, options) => {
|
|
110
|
+
await bridge.typeText({ text, delayMs: options.delay });
|
|
111
|
+
});
|
|
112
|
+
cli
|
|
113
|
+
.command('press <key>', 'Press a key or key combo')
|
|
114
|
+
.option('--count [count]', z.number().default(1).describe('How many times to press'))
|
|
115
|
+
.option('--delay [delay]', z.number().describe('Delay between presses in milliseconds'))
|
|
116
|
+
.action(async (key, options) => {
|
|
117
|
+
await bridge.press({ key, count: options.count, delayMs: options.delay });
|
|
118
|
+
});
|
|
119
|
+
cli
|
|
120
|
+
.command('scroll <direction> [amount]', 'Scroll in a direction')
|
|
121
|
+
.option('--at [at]', z.string().describe('Coordinates x,y where scroll happens'))
|
|
122
|
+
.action(async (direction, amount, options) => {
|
|
123
|
+
const parsedDirection = parseDirection(direction);
|
|
124
|
+
if (parsedDirection instanceof Error) {
|
|
125
|
+
throw parsedDirection;
|
|
126
|
+
}
|
|
127
|
+
const at = options.at ? parsePointOrThrow(options.at) : undefined;
|
|
128
|
+
const scrollAmount = amount ? Number(amount) : 300;
|
|
129
|
+
if (!Number.isFinite(scrollAmount)) {
|
|
130
|
+
throw new Error(`Invalid amount \"${amount}\"`);
|
|
131
|
+
}
|
|
132
|
+
await bridge.scroll({
|
|
133
|
+
direction: parsedDirection,
|
|
134
|
+
amount: scrollAmount,
|
|
135
|
+
at,
|
|
136
|
+
});
|
|
137
|
+
});
|
|
138
|
+
cli
|
|
139
|
+
.command('drag <from> <to>', 'Drag from one coordinate to another')
|
|
140
|
+
.option('--duration [duration]', z.number().describe('Duration in milliseconds'))
|
|
141
|
+
.option('--button [button]', z.enum(['left', 'right', 'middle']).default('left').describe('Mouse button'))
|
|
142
|
+
.action(async (from, to, options) => {
|
|
143
|
+
await bridge.drag({
|
|
144
|
+
from: parsePointOrThrow(from),
|
|
145
|
+
to: parsePointOrThrow(to),
|
|
146
|
+
durationMs: options.duration,
|
|
147
|
+
button: options.button,
|
|
148
|
+
});
|
|
149
|
+
});
|
|
150
|
+
cli
|
|
151
|
+
.command('hover [target]', 'Move mouse cursor to coordinates without clicking')
|
|
152
|
+
.option('-x [x]', z.number().describe('X coordinate'))
|
|
153
|
+
.option('-y [y]', z.number().describe('Y coordinate'))
|
|
154
|
+
.action(async (target, options) => {
|
|
155
|
+
const point = resolvePointInput({
|
|
156
|
+
x: options.x,
|
|
157
|
+
y: options.y,
|
|
158
|
+
target,
|
|
159
|
+
command: 'hover',
|
|
160
|
+
});
|
|
161
|
+
await bridge.hover(point);
|
|
162
|
+
});
|
|
163
|
+
cli
|
|
164
|
+
.command('mouse move [x] [y]', 'Move mouse cursor to absolute coordinates')
|
|
165
|
+
.option('-x [x]', z.number().describe('X coordinate'))
|
|
166
|
+
.option('-y [y]', z.number().describe('Y coordinate'))
|
|
167
|
+
.action(async (x, y, options) => {
|
|
168
|
+
const point = resolvePointInput({
|
|
169
|
+
x: options.x,
|
|
170
|
+
y: options.y,
|
|
171
|
+
target: x && y ? `${x},${y}` : undefined,
|
|
172
|
+
command: 'mouse move',
|
|
173
|
+
});
|
|
174
|
+
await bridge.mouseMove(point);
|
|
175
|
+
});
|
|
176
|
+
cli
|
|
177
|
+
.command('mouse down', 'Press and hold mouse button')
|
|
178
|
+
.option('--button [button]', z.enum(['left', 'right', 'middle']).default('left').describe('Mouse button'))
|
|
179
|
+
.action(async (options) => {
|
|
180
|
+
await bridge.mouseDown({ button: parseButton(options.button) });
|
|
181
|
+
});
|
|
182
|
+
cli
|
|
183
|
+
.command('mouse up', 'Release mouse button')
|
|
184
|
+
.option('--button [button]', z.enum(['left', 'right', 'middle']).default('left').describe('Mouse button'))
|
|
185
|
+
.action(async (options) => {
|
|
186
|
+
await bridge.mouseUp({ button: parseButton(options.button) });
|
|
187
|
+
});
|
|
188
|
+
cli
|
|
189
|
+
.command('mouse position', 'Print current mouse position as x,y')
|
|
190
|
+
.option('--json', 'Output as JSON')
|
|
191
|
+
.action(async (options) => {
|
|
192
|
+
const position = await bridge.mousePosition();
|
|
193
|
+
if (options.json) {
|
|
194
|
+
printJson(position);
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
197
|
+
printLine(`${position.x},${position.y}`);
|
|
198
|
+
});
|
|
199
|
+
cli
|
|
200
|
+
.command('display list', 'List connected displays')
|
|
201
|
+
.option('--json', 'Output as JSON')
|
|
202
|
+
.action(async (options) => {
|
|
203
|
+
const displays = await bridge.displayList();
|
|
204
|
+
if (options.json) {
|
|
205
|
+
printJson(displays);
|
|
206
|
+
return;
|
|
207
|
+
}
|
|
208
|
+
displays.forEach((display) => {
|
|
209
|
+
const primary = display.isPrimary ? ' (primary)' : '';
|
|
210
|
+
printLine(`#${display.id} ${display.name}${primary} ${display.width}x${display.height} @ (${display.x},${display.y}) scale=${display.scale}`);
|
|
211
|
+
});
|
|
212
|
+
});
|
|
213
|
+
cli
|
|
214
|
+
.command('clipboard get', 'Print clipboard text')
|
|
215
|
+
.action(async () => {
|
|
216
|
+
const text = await bridge.clipboardGet();
|
|
217
|
+
printLine(text);
|
|
218
|
+
});
|
|
219
|
+
cli
|
|
220
|
+
.command('clipboard set <text>', 'Set clipboard text')
|
|
221
|
+
.action(async (text) => {
|
|
222
|
+
await bridge.clipboardSet({ text });
|
|
223
|
+
});
|
|
224
|
+
cli.command('snapshot').action(() => {
|
|
225
|
+
notImplemented({ command: 'snapshot' });
|
|
226
|
+
});
|
|
227
|
+
cli.command('get text <target>').action(() => {
|
|
228
|
+
notImplemented({ command: 'get text' });
|
|
229
|
+
});
|
|
230
|
+
cli.command('get title <target>').action(() => {
|
|
231
|
+
notImplemented({ command: 'get title' });
|
|
232
|
+
});
|
|
233
|
+
cli.command('get value <target>').action(() => {
|
|
234
|
+
notImplemented({ command: 'get value' });
|
|
235
|
+
});
|
|
236
|
+
cli.command('get bounds <target>').action(() => {
|
|
237
|
+
notImplemented({ command: 'get bounds' });
|
|
238
|
+
});
|
|
239
|
+
cli.command('get focused').action(() => {
|
|
240
|
+
notImplemented({ command: 'get focused' });
|
|
241
|
+
});
|
|
242
|
+
cli.command('window list').action(() => {
|
|
243
|
+
notImplemented({ command: 'window list' });
|
|
244
|
+
});
|
|
245
|
+
cli.command('window focus <target>').action(() => {
|
|
246
|
+
notImplemented({ command: 'window focus' });
|
|
247
|
+
});
|
|
248
|
+
cli.command('window resize <target> <width> <height>').action(() => {
|
|
249
|
+
notImplemented({ command: 'window resize' });
|
|
250
|
+
});
|
|
251
|
+
cli.command('window move <target> <x> <y>').action(() => {
|
|
252
|
+
notImplemented({ command: 'window move' });
|
|
253
|
+
});
|
|
254
|
+
cli.command('window minimize <target>').action(() => {
|
|
255
|
+
notImplemented({ command: 'window minimize' });
|
|
256
|
+
});
|
|
257
|
+
cli.command('window maximize <target>').action(() => {
|
|
258
|
+
notImplemented({ command: 'window maximize' });
|
|
259
|
+
});
|
|
260
|
+
cli.command('window close <target>').action(() => {
|
|
261
|
+
notImplemented({ command: 'window close' });
|
|
262
|
+
});
|
|
263
|
+
cli.command('app list').action(() => {
|
|
264
|
+
notImplemented({ command: 'app list' });
|
|
265
|
+
});
|
|
266
|
+
cli.command('app launch <name>').action(() => {
|
|
267
|
+
notImplemented({ command: 'app launch' });
|
|
268
|
+
});
|
|
269
|
+
cli.command('app quit <name>').action(() => {
|
|
270
|
+
notImplemented({ command: 'app quit' });
|
|
271
|
+
});
|
|
272
|
+
cli.command('wait <target>').action(() => {
|
|
273
|
+
notImplemented({ command: 'wait' });
|
|
274
|
+
});
|
|
275
|
+
cli.command('find <query>').action(() => {
|
|
276
|
+
notImplemented({ command: 'find' });
|
|
277
|
+
});
|
|
278
|
+
cli.command('diff snapshot').action(() => {
|
|
279
|
+
notImplemented({ command: 'diff snapshot' });
|
|
280
|
+
});
|
|
281
|
+
cli.command('diff screenshot').action(() => {
|
|
282
|
+
notImplemented({ command: 'diff screenshot' });
|
|
283
|
+
});
|
|
284
|
+
cli.help();
|
|
285
|
+
cli.version(packageJson.version);
|
|
286
|
+
return cli;
|
|
287
|
+
}
|
|
288
|
+
export function runCli() {
|
|
289
|
+
const cli = createCli();
|
|
290
|
+
cli.parse();
|
|
291
|
+
}
|
|
292
|
+
const isDirectEntrypoint = (() => {
|
|
293
|
+
const argvPath = process.argv[1];
|
|
294
|
+
if (!argvPath) {
|
|
295
|
+
return false;
|
|
296
|
+
}
|
|
297
|
+
return import.meta.url === url.pathToFileURL(argvPath).href;
|
|
298
|
+
})();
|
|
299
|
+
if (isDirectEntrypoint) {
|
|
300
|
+
runCli();
|
|
301
|
+
}
|