usecomputer 0.0.3 → 0.0.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/README.md +324 -0
- package/dist/bridge-contract.test.js +124 -63
- package/dist/bridge.d.ts.map +1 -1
- package/dist/bridge.js +241 -46
- package/dist/cli-parsing.test.js +34 -11
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +323 -28
- package/dist/coord-map.d.ts +14 -0
- package/dist/coord-map.d.ts.map +1 -0
- package/dist/coord-map.js +75 -0
- package/dist/coord-map.test.d.ts +2 -0
- package/dist/coord-map.test.d.ts.map +1 -0
- package/dist/coord-map.test.js +157 -0
- package/dist/darwin-arm64/usecomputer.node +0 -0
- package/dist/darwin-x64/usecomputer.node +0 -0
- package/dist/debug-point-image.d.ts +8 -0
- package/dist/debug-point-image.d.ts.map +1 -0
- package/dist/debug-point-image.js +43 -0
- package/dist/debug-point-image.test.d.ts +2 -0
- package/dist/debug-point-image.test.d.ts.map +1 -0
- package/dist/debug-point-image.test.js +44 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -1
- package/dist/lib.d.ts +26 -0
- package/dist/lib.d.ts.map +1 -0
- package/dist/lib.js +88 -0
- package/dist/native-click-smoke.test.js +69 -29
- package/dist/native-lib.d.ts +59 -1
- package/dist/native-lib.d.ts.map +1 -1
- package/dist/terminal-table.d.ts +10 -0
- package/dist/terminal-table.d.ts.map +1 -0
- package/dist/terminal-table.js +55 -0
- package/dist/terminal-table.test.d.ts +2 -0
- package/dist/terminal-table.test.d.ts.map +1 -0
- package/dist/terminal-table.test.js +41 -0
- package/dist/types.d.ts +45 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +16 -4
- package/src/bridge-contract.test.ts +140 -69
- package/src/bridge.ts +293 -53
- package/src/cli-parsing.test.ts +61 -0
- package/src/cli.ts +393 -32
- package/src/coord-map.test.ts +178 -0
- package/src/coord-map.ts +105 -0
- package/src/debug-point-image.test.ts +50 -0
- package/src/debug-point-image.ts +69 -0
- package/src/index.ts +3 -1
- package/src/lib.ts +125 -0
- package/src/native-click-smoke.test.ts +81 -63
- package/src/native-lib.ts +39 -1
- package/src/terminal-table.test.ts +44 -0
- package/src/terminal-table.ts +88 -0
- package/src/types.ts +50 -0
- package/zig/src/lib.zig +1258 -267
- package/zig/src/scroll.zig +213 -0
- package/zig/src/window.zig +123 -0
package/README.md
CHANGED
|
@@ -7,6 +7,14 @@
|
|
|
7
7
|
It can move the mouse, click, drag, and query cursor position using native
|
|
8
8
|
Quartz events through a Zig N-API module.
|
|
9
9
|
|
|
10
|
+
Keyboard synthesis (`type` and `press`) is also available. The native backend
|
|
11
|
+
includes platform-specific key injection paths for macOS, Windows, and Linux
|
|
12
|
+
X11.
|
|
13
|
+
|
|
14
|
+
The package also exports the native commands as plain library functions, so you
|
|
15
|
+
can `import * as usecomputer from "usecomputer"` and reuse the same screenshot,
|
|
16
|
+
mouse, keyboard, and coord-map behavior from Node.js.
|
|
17
|
+
|
|
10
18
|
## Install
|
|
11
19
|
|
|
12
20
|
```bash
|
|
@@ -24,8 +32,321 @@ npm install -g usecomputer
|
|
|
24
32
|
usecomputer mouse position --json
|
|
25
33
|
usecomputer mouse move -x 500 -y 500
|
|
26
34
|
usecomputer click -x 500 -y 500 --button left --count 1
|
|
35
|
+
usecomputer type "hello"
|
|
36
|
+
usecomputer press "cmd+s"
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## Library usage
|
|
40
|
+
|
|
41
|
+
```ts
|
|
42
|
+
import * as usecomputer from 'usecomputer'
|
|
43
|
+
|
|
44
|
+
const screenshot = await usecomputer.screenshot({
|
|
45
|
+
path: './tmp/shot.png',
|
|
46
|
+
display: null,
|
|
47
|
+
window: null,
|
|
48
|
+
region: null,
|
|
49
|
+
annotate: null,
|
|
50
|
+
})
|
|
51
|
+
|
|
52
|
+
const coordMap = usecomputer.parseCoordMapOrThrow(screenshot.coordMap)
|
|
53
|
+
const point = usecomputer.mapPointFromCoordMap({
|
|
54
|
+
point: { x: 400, y: 220 },
|
|
55
|
+
coordMap,
|
|
56
|
+
})
|
|
57
|
+
|
|
58
|
+
await usecomputer.click({
|
|
59
|
+
point,
|
|
60
|
+
button: 'left',
|
|
61
|
+
count: 1,
|
|
62
|
+
})
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
These exported functions intentionally mirror the native command shapes used by
|
|
66
|
+
the Zig N-API module. Optional native fields are passed as `null` when absent.
|
|
67
|
+
|
|
68
|
+
## OpenAI computer tool example
|
|
69
|
+
|
|
70
|
+
```ts
|
|
71
|
+
import fs from 'node:fs'
|
|
72
|
+
import * as usecomputer from 'usecomputer'
|
|
73
|
+
|
|
74
|
+
async function sendComputerScreenshot() {
|
|
75
|
+
const screenshot = await usecomputer.screenshot({
|
|
76
|
+
path: './tmp/computer-tool.png',
|
|
77
|
+
display: null,
|
|
78
|
+
window: null,
|
|
79
|
+
region: null,
|
|
80
|
+
annotate: null,
|
|
81
|
+
})
|
|
82
|
+
|
|
83
|
+
return {
|
|
84
|
+
screenshot,
|
|
85
|
+
imageBase64: await fs.promises.readFile(screenshot.path, 'base64'),
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
async function runComputerAction(action, coordMap) {
|
|
90
|
+
if (action.type === 'click') {
|
|
91
|
+
await usecomputer.click({
|
|
92
|
+
point: usecomputer.mapPointFromCoordMap({
|
|
93
|
+
point: { x: action.x, y: action.y },
|
|
94
|
+
coordMap: usecomputer.parseCoordMapOrThrow(coordMap),
|
|
95
|
+
}),
|
|
96
|
+
button: action.button ?? 'left',
|
|
97
|
+
count: 1,
|
|
98
|
+
})
|
|
99
|
+
return
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
if (action.type === 'double_click') {
|
|
103
|
+
await usecomputer.click({
|
|
104
|
+
point: usecomputer.mapPointFromCoordMap({
|
|
105
|
+
point: { x: action.x, y: action.y },
|
|
106
|
+
coordMap: usecomputer.parseCoordMapOrThrow(coordMap),
|
|
107
|
+
}),
|
|
108
|
+
button: action.button ?? 'left',
|
|
109
|
+
count: 2,
|
|
110
|
+
})
|
|
111
|
+
return
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
if (action.type === 'scroll') {
|
|
115
|
+
await usecomputer.scroll({
|
|
116
|
+
direction: action.scrollY && action.scrollY < 0 ? 'up' : 'down',
|
|
117
|
+
amount: Math.abs(action.scrollY ?? 0),
|
|
118
|
+
at: typeof action.x === 'number' && typeof action.y === 'number'
|
|
119
|
+
? usecomputer.mapPointFromCoordMap({
|
|
120
|
+
point: { x: action.x, y: action.y },
|
|
121
|
+
coordMap: usecomputer.parseCoordMapOrThrow(coordMap),
|
|
122
|
+
})
|
|
123
|
+
: null,
|
|
124
|
+
})
|
|
125
|
+
return
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
if (action.type === 'keypress') {
|
|
129
|
+
await usecomputer.press({
|
|
130
|
+
key: action.keys.join('+'),
|
|
131
|
+
count: 1,
|
|
132
|
+
delayMs: null,
|
|
133
|
+
})
|
|
134
|
+
return
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
if (action.type === 'type') {
|
|
138
|
+
await usecomputer.typeText({
|
|
139
|
+
text: action.text,
|
|
140
|
+
delayMs: null,
|
|
141
|
+
})
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
## Anthropic computer use example
|
|
147
|
+
|
|
148
|
+
Anthropic's computer tool uses action names like `left_click`, `double_click`,
|
|
149
|
+
`mouse_move`, `key`, `type`, `scroll`, and `screenshot`. `usecomputer`
|
|
150
|
+
provides the execution layer for those actions.
|
|
151
|
+
|
|
152
|
+
```ts
|
|
153
|
+
import fs from 'node:fs'
|
|
154
|
+
import Anthropic from '@anthropic-ai/sdk'
|
|
155
|
+
import type {
|
|
156
|
+
BetaToolResultBlockParam,
|
|
157
|
+
BetaToolUseBlock,
|
|
158
|
+
} from '@anthropic-ai/sdk/resources/beta/messages/messages'
|
|
159
|
+
import * as usecomputer from 'usecomputer'
|
|
160
|
+
|
|
161
|
+
const anthropic = new Anthropic({ apiKey: process.env.ANTHROPIC_API_KEY })
|
|
162
|
+
|
|
163
|
+
const message = await anthropic.beta.messages.create({
|
|
164
|
+
model: 'claude-opus-4-6',
|
|
165
|
+
max_tokens: 1024,
|
|
166
|
+
tools: [
|
|
167
|
+
{
|
|
168
|
+
type: 'computer_20251124',
|
|
169
|
+
name: 'computer',
|
|
170
|
+
display_width_px: 1024,
|
|
171
|
+
display_height_px: 768,
|
|
172
|
+
display_number: 1,
|
|
173
|
+
},
|
|
174
|
+
],
|
|
175
|
+
messages: [{ role: 'user', content: 'Open Safari and search for usecomputer.' }],
|
|
176
|
+
betas: ['computer-use-2025-11-24'],
|
|
177
|
+
})
|
|
178
|
+
|
|
179
|
+
for (const block of message.content) {
|
|
180
|
+
if (block.type !== 'tool_use' || block.name !== 'computer') {
|
|
181
|
+
continue
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
const toolUse = block as BetaToolUseBlock
|
|
185
|
+
await usecomputer.screenshot({
|
|
186
|
+
path: './tmp/claude-current-screen.png',
|
|
187
|
+
display: null,
|
|
188
|
+
window: null,
|
|
189
|
+
region: null,
|
|
190
|
+
annotate: null,
|
|
191
|
+
})
|
|
192
|
+
const coordinate = Array.isArray(toolUse.input.coordinate)
|
|
193
|
+
? toolUse.input.coordinate
|
|
194
|
+
: null
|
|
195
|
+
const point = coordinate
|
|
196
|
+
? { x: coordinate[0] ?? 0, y: coordinate[1] ?? 0 }
|
|
197
|
+
: null
|
|
198
|
+
|
|
199
|
+
switch (toolUse.input.action) {
|
|
200
|
+
case 'screenshot': {
|
|
201
|
+
break
|
|
202
|
+
}
|
|
203
|
+
case 'left_click': {
|
|
204
|
+
if (point) {
|
|
205
|
+
await usecomputer.click({ point, button: 'left', count: 1 })
|
|
206
|
+
}
|
|
207
|
+
break
|
|
208
|
+
}
|
|
209
|
+
case 'double_click': {
|
|
210
|
+
if (point) {
|
|
211
|
+
await usecomputer.click({ point, button: 'left', count: 2 })
|
|
212
|
+
}
|
|
213
|
+
break
|
|
214
|
+
}
|
|
215
|
+
case 'mouse_move': {
|
|
216
|
+
if (point) {
|
|
217
|
+
await usecomputer.mouseMove(point)
|
|
218
|
+
}
|
|
219
|
+
break
|
|
220
|
+
}
|
|
221
|
+
case 'type': {
|
|
222
|
+
if (typeof toolUse.input.text === 'string') {
|
|
223
|
+
await usecomputer.typeText({ text: toolUse.input.text, delayMs: null })
|
|
224
|
+
}
|
|
225
|
+
break
|
|
226
|
+
}
|
|
227
|
+
case 'key': {
|
|
228
|
+
if (typeof toolUse.input.text === 'string') {
|
|
229
|
+
await usecomputer.press({ key: toolUse.input.text, count: 1, delayMs: null })
|
|
230
|
+
}
|
|
231
|
+
break
|
|
232
|
+
}
|
|
233
|
+
case 'scroll': {
|
|
234
|
+
await usecomputer.scroll({
|
|
235
|
+
direction: toolUse.input.scroll_direction === 'up' || toolUse.input.scroll_direction === 'down' || toolUse.input.scroll_direction === 'left' || toolUse.input.scroll_direction === 'right'
|
|
236
|
+
? toolUse.input.scroll_direction
|
|
237
|
+
: 'down',
|
|
238
|
+
amount: typeof toolUse.input.scroll_amount === 'number' ? toolUse.input.scroll_amount : 3,
|
|
239
|
+
at: point,
|
|
240
|
+
})
|
|
241
|
+
break
|
|
242
|
+
}
|
|
243
|
+
default: {
|
|
244
|
+
throw new Error(`Unsupported Claude computer action: ${String(toolUse.input.action)}`)
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
const afterActionScreenshot = await usecomputer.screenshot({
|
|
249
|
+
path: './tmp/claude-computer-tool.png',
|
|
250
|
+
display: null,
|
|
251
|
+
window: null,
|
|
252
|
+
region: null,
|
|
253
|
+
annotate: null,
|
|
254
|
+
})
|
|
255
|
+
const imageBase64 = await fs.promises.readFile(afterActionScreenshot.path, 'base64')
|
|
256
|
+
const toolResult: BetaToolResultBlockParam = {
|
|
257
|
+
type: 'tool_result',
|
|
258
|
+
tool_use_id: toolUse.id,
|
|
259
|
+
content: [
|
|
260
|
+
{
|
|
261
|
+
type: 'image',
|
|
262
|
+
source: {
|
|
263
|
+
type: 'base64',
|
|
264
|
+
media_type: 'image/png',
|
|
265
|
+
data: imageBase64,
|
|
266
|
+
},
|
|
267
|
+
},
|
|
268
|
+
],
|
|
269
|
+
}
|
|
270
|
+
// Append toolResult to the next user message in your agent loop.
|
|
271
|
+
}
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
## Screenshot scaling and coord-map
|
|
275
|
+
|
|
276
|
+
`usecomputer screenshot` always scales the output image so the longest edge is
|
|
277
|
+
at most `1568` px. This keeps screenshots in a model-friendly size for
|
|
278
|
+
computer-use agents.
|
|
279
|
+
|
|
280
|
+
Screenshot output includes:
|
|
281
|
+
|
|
282
|
+
- `desktopIndex` (display index used for capture)
|
|
283
|
+
- `coordMap` in the form `captureX,captureY,captureWidth,captureHeight,imageWidth,imageHeight`
|
|
284
|
+
- `hint` with usage text for coordinate mapping
|
|
285
|
+
|
|
286
|
+
Always pass the exact `--coord-map` value emitted by `usecomputer screenshot`
|
|
287
|
+
to pointer commands when you are clicking coordinates from that screenshot.
|
|
288
|
+
This maps screenshot-space coordinates back to real screen coordinates:
|
|
289
|
+
|
|
290
|
+
```bash
|
|
291
|
+
usecomputer screenshot ./shot.png --json
|
|
292
|
+
usecomputer click -x 400 -y 220 --coord-map "0,0,1600,900,1568,882"
|
|
293
|
+
usecomputer mouse move -x 100 -y 80 --coord-map "0,0,1600,900,1568,882"
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
To validate a target before clicking, use `debug-point`. It takes the same
|
|
297
|
+
coordinates and `--coord-map`, captures a fresh full-desktop screenshot, and
|
|
298
|
+
draws a red marker where the click would land. When `--coord-map` is present,
|
|
299
|
+
it captures that same region so the overlay matches the screenshot you are
|
|
300
|
+
targeting:
|
|
301
|
+
|
|
302
|
+
```bash
|
|
303
|
+
usecomputer debug-point -x 400 -y 220 --coord-map "0,0,1600,900,1568,882"
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
## Keyboard commands
|
|
307
|
+
|
|
308
|
+
### Type text
|
|
309
|
+
|
|
310
|
+
```bash
|
|
311
|
+
# Short text
|
|
312
|
+
usecomputer type "hello from usecomputer"
|
|
313
|
+
|
|
314
|
+
# Type from stdin (good for multiline or very long text)
|
|
315
|
+
cat ./notes.txt | usecomputer type --stdin --chunk-size 4000 --chunk-delay 15
|
|
316
|
+
|
|
317
|
+
# Simulate slower typing for apps that drop fast input
|
|
318
|
+
usecomputer type "hello" --delay 20
|
|
27
319
|
```
|
|
28
320
|
|
|
321
|
+
`--delay` is the per-character delay in milliseconds.
|
|
322
|
+
|
|
323
|
+
For very long text, prefer `--stdin` + `--chunk-size` so shell argument limits
|
|
324
|
+
and app input buffers are less likely to cause dropped characters.
|
|
325
|
+
|
|
326
|
+
### Press keys and shortcuts
|
|
327
|
+
|
|
328
|
+
```bash
|
|
329
|
+
# Single key
|
|
330
|
+
usecomputer press "enter"
|
|
331
|
+
|
|
332
|
+
# Chords
|
|
333
|
+
usecomputer press "cmd+s"
|
|
334
|
+
usecomputer press "cmd+shift+p"
|
|
335
|
+
usecomputer press "ctrl+s"
|
|
336
|
+
|
|
337
|
+
# Repeats
|
|
338
|
+
usecomputer press "down" --count 10 --delay 30
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
Modifier aliases: `cmd`/`command`/`meta`, `ctrl`/`control`, `alt`/`option`,
|
|
342
|
+
`shift`, `fn`.
|
|
343
|
+
|
|
344
|
+
Platform note:
|
|
345
|
+
|
|
346
|
+
- macOS: `cmd` maps to Command.
|
|
347
|
+
- Windows/Linux: `cmd` maps to Win/Super.
|
|
348
|
+
- For app shortcuts that should work on Windows/Linux too, prefer `ctrl+...`.
|
|
349
|
+
|
|
29
350
|
## Coordinate options
|
|
30
351
|
|
|
31
352
|
Commands that target coordinates accept `-x` and `-y` flags:
|
|
@@ -34,6 +355,9 @@ Commands that target coordinates accept `-x` and `-y` flags:
|
|
|
34
355
|
- `usecomputer hover -x <n> -y <n>`
|
|
35
356
|
- `usecomputer mouse move -x <n> -y <n>`
|
|
36
357
|
|
|
358
|
+
`mouse move` is optional before `click` when click coordinates are already
|
|
359
|
+
provided.
|
|
360
|
+
|
|
37
361
|
Legacy coordinate forms are also accepted where available.
|
|
38
362
|
|
|
39
363
|
## Display index options
|
|
@@ -1,74 +1,135 @@
|
|
|
1
|
-
// Contract tests for
|
|
1
|
+
// Contract tests for direct native method calls emitted by the TS bridge.
|
|
2
|
+
// These tests intentionally call the real Zig native module.
|
|
2
3
|
import { describe, expect, test } from 'vitest';
|
|
3
4
|
import { createBridgeFromNative } from './bridge.js';
|
|
4
|
-
|
|
5
|
-
return {
|
|
6
|
-
execute(command, payloadJson) {
|
|
7
|
-
const payload = JSON.parse(payloadJson);
|
|
8
|
-
calls.push({ command, payload });
|
|
9
|
-
if (command === 'mouse-position') {
|
|
10
|
-
return JSON.stringify({ ok: true, data: { x: 10, y: 20 } });
|
|
11
|
-
}
|
|
12
|
-
if (command === 'display-list') {
|
|
13
|
-
return JSON.stringify({
|
|
14
|
-
ok: true,
|
|
15
|
-
data: [
|
|
16
|
-
{ id: 1, name: 'Built-in', x: 0, y: 0, width: 1512, height: 982, scale: 2, isPrimary: true },
|
|
17
|
-
],
|
|
18
|
-
});
|
|
19
|
-
}
|
|
20
|
-
if (command === 'clipboard-get') {
|
|
21
|
-
return JSON.stringify({ ok: true, data: { text: 'hello' } });
|
|
22
|
-
}
|
|
23
|
-
if (command === 'screenshot') {
|
|
24
|
-
return JSON.stringify({ ok: true, data: { path: '/tmp/test.png' } });
|
|
25
|
-
}
|
|
26
|
-
return JSON.stringify({ ok: true, data: null });
|
|
27
|
-
},
|
|
28
|
-
};
|
|
29
|
-
}
|
|
5
|
+
import { native } from './native-lib.js';
|
|
30
6
|
describe('native bridge contract', () => {
|
|
31
|
-
test('
|
|
32
|
-
|
|
33
|
-
|
|
7
|
+
test('bridge calls hit real Zig module', async () => {
|
|
8
|
+
expect(native).toBeTruthy();
|
|
9
|
+
if (!native) {
|
|
10
|
+
return;
|
|
11
|
+
}
|
|
12
|
+
const bridge = createBridgeFromNative({ nativeModule: native });
|
|
13
|
+
const safeTarget = {
|
|
14
|
+
x: 0,
|
|
15
|
+
y: 0,
|
|
16
|
+
};
|
|
34
17
|
await bridge.click({
|
|
35
|
-
point:
|
|
18
|
+
point: safeTarget,
|
|
36
19
|
button: 'left',
|
|
37
|
-
count:
|
|
38
|
-
modifiers: [
|
|
20
|
+
count: 1,
|
|
21
|
+
modifiers: [],
|
|
39
22
|
});
|
|
40
|
-
await bridge.
|
|
41
|
-
await bridge.
|
|
42
|
-
await bridge.scroll({ direction: 'down', amount: 300 });
|
|
43
|
-
await bridge.drag({ from: { x: 10, y: 10 }, to: { x: 200, y: 120 }, button: 'left' });
|
|
44
|
-
await bridge.hover({ x: 33, y: 44 });
|
|
45
|
-
await bridge.mouseMove({ x: 60, y: 70 });
|
|
23
|
+
await bridge.hover(safeTarget);
|
|
24
|
+
await bridge.mouseMove(safeTarget);
|
|
46
25
|
await bridge.mouseDown({ button: 'left' });
|
|
47
26
|
await bridge.mouseUp({ button: 'left' });
|
|
48
|
-
await bridge.
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
})
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
27
|
+
await bridge.drag({
|
|
28
|
+
from: safeTarget,
|
|
29
|
+
to: { x: safeTarget.x + 6, y: safeTarget.y + 6 },
|
|
30
|
+
button: 'left',
|
|
31
|
+
durationMs: 10,
|
|
32
|
+
});
|
|
33
|
+
const screenshot = await bridge.screenshot({ path: `${process.cwd()}/tmp/bridge-contract-shot.png` });
|
|
34
|
+
await bridge.typeText({ text: 'h', delayMs: 30 });
|
|
35
|
+
await bridge.press({ key: 'backspace', count: 1 });
|
|
36
|
+
const scrollResult = await bridge.scroll({ direction: 'down', amount: 1 }).then(() => {
|
|
37
|
+
return 'ok';
|
|
38
|
+
}, (error) => {
|
|
39
|
+
return error instanceof Error ? error.message : String(error);
|
|
40
|
+
});
|
|
41
|
+
const scrollAtResult = await bridge.scroll({ direction: 'right', amount: 1, at: safeTarget }).then(() => {
|
|
42
|
+
return 'ok';
|
|
43
|
+
}, (error) => {
|
|
44
|
+
return error instanceof Error ? error.message : String(error);
|
|
45
|
+
});
|
|
46
|
+
const displays = await bridge.displayList();
|
|
47
|
+
const windows = await bridge.windowList();
|
|
48
|
+
const clipboardGetResult = await bridge.clipboardGet().then(() => {
|
|
49
|
+
return 'ok';
|
|
50
|
+
}, (error) => {
|
|
51
|
+
return error instanceof Error ? error.message : String(error);
|
|
52
|
+
});
|
|
53
|
+
const clipboardSetResult = await bridge.clipboardSet({ text: 'copied' }).then(() => {
|
|
54
|
+
return 'ok';
|
|
55
|
+
}, (error) => {
|
|
56
|
+
return error instanceof Error ? error.message : String(error);
|
|
57
|
+
});
|
|
58
|
+
const isOkOrTodo = ({ value }) => {
|
|
59
|
+
return value === 'ok' || value.includes('TODO not implemented');
|
|
60
|
+
};
|
|
61
|
+
expect({
|
|
62
|
+
screenshotShape: {
|
|
63
|
+
path: screenshot.path,
|
|
64
|
+
desktopIndex: typeof screenshot.desktopIndex,
|
|
65
|
+
captureX: typeof screenshot.captureX,
|
|
66
|
+
captureY: typeof screenshot.captureY,
|
|
67
|
+
captureWidth: screenshot.captureWidth > 0,
|
|
68
|
+
captureHeight: screenshot.captureHeight > 0,
|
|
69
|
+
imageWidth: screenshot.imageWidth > 0,
|
|
70
|
+
imageHeight: screenshot.imageHeight > 0,
|
|
71
|
+
coordMapHasSixValues: screenshot.coordMap.split(',').length === 6,
|
|
72
|
+
hint: screenshot.hint,
|
|
73
|
+
},
|
|
74
|
+
firstDisplayShape: displays[0]
|
|
75
|
+
? {
|
|
76
|
+
id: typeof displays[0].id,
|
|
77
|
+
index: typeof displays[0].index,
|
|
78
|
+
width: displays[0].width > 0,
|
|
79
|
+
height: displays[0].height > 0,
|
|
80
|
+
}
|
|
81
|
+
: null,
|
|
82
|
+
firstWindowShape: windows[0]
|
|
83
|
+
? {
|
|
84
|
+
id: typeof windows[0].id,
|
|
85
|
+
ownerName: typeof windows[0].ownerName,
|
|
86
|
+
desktopIndex: typeof windows[0].desktopIndex,
|
|
87
|
+
}
|
|
88
|
+
: null,
|
|
89
|
+
optionalCommandOutcomes: {
|
|
90
|
+
scrollResult: isOkOrTodo({ value: scrollResult }),
|
|
91
|
+
scrollAtResult: isOkOrTodo({ value: scrollAtResult }),
|
|
92
|
+
clipboardGetResult: isOkOrTodo({ value: clipboardGetResult }),
|
|
93
|
+
clipboardSetResult: isOkOrTodo({ value: clipboardSetResult }),
|
|
94
|
+
},
|
|
95
|
+
}).toMatchInlineSnapshot(`
|
|
96
|
+
{
|
|
97
|
+
"firstDisplayShape": {
|
|
98
|
+
"height": true,
|
|
99
|
+
"id": "number",
|
|
100
|
+
"index": "number",
|
|
101
|
+
"width": true,
|
|
102
|
+
},
|
|
103
|
+
"firstWindowShape": {
|
|
104
|
+
"desktopIndex": "number",
|
|
105
|
+
"id": "number",
|
|
106
|
+
"ownerName": "string",
|
|
107
|
+
},
|
|
108
|
+
"optionalCommandOutcomes": {
|
|
109
|
+
"clipboardGetResult": true,
|
|
110
|
+
"clipboardSetResult": true,
|
|
111
|
+
"scrollAtResult": true,
|
|
112
|
+
"scrollResult": true,
|
|
113
|
+
},
|
|
114
|
+
"screenshotShape": {
|
|
115
|
+
"captureHeight": true,
|
|
116
|
+
"captureWidth": true,
|
|
117
|
+
"captureX": "number",
|
|
118
|
+
"captureY": "number",
|
|
119
|
+
"coordMapHasSixValues": true,
|
|
120
|
+
"desktopIndex": "number",
|
|
121
|
+
"hint": "ALWAYS pass this exact coord map to click, hover, drag, and mouse move when using coordinates from this screenshot:
|
|
122
|
+
--coord-map "0,0,3440,1440,1568,656"
|
|
123
|
+
|
|
124
|
+
Example:
|
|
125
|
+
usecomputer click -x 400 -y 220 --coord-map "0,0,3440,1440,1568,656"",
|
|
126
|
+
"imageHeight": true,
|
|
127
|
+
"imageWidth": true,
|
|
128
|
+
"path": "/Users/morse/Documents/GitHub/kimakivoice/usecomputer/tmp/bridge-contract-shot.png",
|
|
129
|
+
},
|
|
130
|
+
}
|
|
72
131
|
`);
|
|
132
|
+
expect(displays.length).toBeGreaterThan(0);
|
|
133
|
+
expect(windows.length).toBeGreaterThan(0);
|
|
73
134
|
});
|
|
74
135
|
});
|
package/dist/bridge.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bridge.d.ts","sourceRoot":"","sources":["../src/bridge.ts"],"names":[],"mappings":"AAEA,OAAO,EAAU,KAAK,YAAY,EAAE,MAAM,iBAAiB,CAAA;
|
|
1
|
+
{"version":3,"file":"bridge.d.ts","sourceRoot":"","sources":["../src/bridge.ts"],"names":[],"mappings":"AAEA,OAAO,EAAU,KAAK,YAAY,EAAE,MAAM,iBAAiB,CAAA;AAE3D,OAAO,KAAK,EAaV,iBAAiB,EAElB,MAAM,YAAY,CAAA;AAsHnB,wBAAgB,sBAAsB,CAAC,EAAE,YAAY,EAAE,EAAE;IAAE,YAAY,EAAE,YAAY,GAAG,IAAI,CAAA;CAAE,GAAG,iBAAiB,CAiQjH;AAED,wBAAgB,YAAY,IAAI,iBAAiB,CAEhD"}
|