usecomputer 0.0.2 → 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/CHANGELOG.md +13 -0
- package/README.md +338 -0
- package/build.zig +1 -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 +328 -22
- 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 +401 -25
- 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 +1280 -163
- package/zig/src/scroll.zig +213 -0
- package/zig/src/window.zig +123 -0
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
// Generic aligned terminal table renderer for CLI command output.
|
|
2
|
+
export function renderAlignedTable({ rows, columns, }) {
|
|
3
|
+
if (columns.length === 0) {
|
|
4
|
+
return [];
|
|
5
|
+
}
|
|
6
|
+
const widthByColumn = columns.map((column) => {
|
|
7
|
+
const rowWidth = rows.reduce((maxWidth, row) => {
|
|
8
|
+
const width = printableWidth(column.value(row));
|
|
9
|
+
return Math.max(maxWidth, width);
|
|
10
|
+
}, 0);
|
|
11
|
+
return Math.max(printableWidth(column.header), rowWidth);
|
|
12
|
+
});
|
|
13
|
+
const formatCell = ({ value, width, align, }) => {
|
|
14
|
+
const currentWidth = printableWidth(value);
|
|
15
|
+
const padSize = Math.max(0, width - currentWidth);
|
|
16
|
+
const padding = ' '.repeat(padSize);
|
|
17
|
+
if (align === 'right') {
|
|
18
|
+
return `${padding}${value}`;
|
|
19
|
+
}
|
|
20
|
+
return `${value}${padding}`;
|
|
21
|
+
};
|
|
22
|
+
const renderRow = ({ values, }) => {
|
|
23
|
+
return values.map((value, index) => {
|
|
24
|
+
const column = columns[index];
|
|
25
|
+
if (!column) {
|
|
26
|
+
return value;
|
|
27
|
+
}
|
|
28
|
+
return formatCell({
|
|
29
|
+
value,
|
|
30
|
+
width: widthByColumn[index] ?? value.length,
|
|
31
|
+
align: column.align ?? 'left',
|
|
32
|
+
});
|
|
33
|
+
}).join(' ');
|
|
34
|
+
};
|
|
35
|
+
const header = renderRow({
|
|
36
|
+
values: columns.map((column) => {
|
|
37
|
+
return column.header;
|
|
38
|
+
}),
|
|
39
|
+
});
|
|
40
|
+
const divider = widthByColumn.map((width) => {
|
|
41
|
+
return '-'.repeat(width);
|
|
42
|
+
}).join(' ');
|
|
43
|
+
const lines = rows.map((row) => {
|
|
44
|
+
return renderRow({
|
|
45
|
+
values: columns.map((column) => {
|
|
46
|
+
return column.value(row);
|
|
47
|
+
}),
|
|
48
|
+
});
|
|
49
|
+
});
|
|
50
|
+
return [header, divider, ...lines];
|
|
51
|
+
}
|
|
52
|
+
function printableWidth(value) {
|
|
53
|
+
const ansiStripped = value.replace(/\u001b\[[0-9;]*m/g, '');
|
|
54
|
+
return ansiStripped.length;
|
|
55
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"terminal-table.test.d.ts","sourceRoot":"","sources":["../src/terminal-table.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
// Tests aligned terminal table formatting for deterministic CLI rendering.
|
|
2
|
+
import { describe, expect, test } from 'vitest';
|
|
3
|
+
import { renderAlignedTable } from './terminal-table.js';
|
|
4
|
+
describe('terminal table', () => {
|
|
5
|
+
test('renders aligned columns for mixed widths', () => {
|
|
6
|
+
const lines = renderAlignedTable({
|
|
7
|
+
rows: [
|
|
8
|
+
{ id: 2, app: 'Zed', size: '1720x1440' },
|
|
9
|
+
{ id: 102, app: 'Google Chrome', size: '3440x1440' },
|
|
10
|
+
],
|
|
11
|
+
columns: [
|
|
12
|
+
{
|
|
13
|
+
header: 'id',
|
|
14
|
+
align: 'right',
|
|
15
|
+
value: (row) => {
|
|
16
|
+
return String(row.id);
|
|
17
|
+
},
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
header: 'app',
|
|
21
|
+
value: (row) => {
|
|
22
|
+
return row.app;
|
|
23
|
+
},
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
header: 'size',
|
|
27
|
+
align: 'right',
|
|
28
|
+
value: (row) => {
|
|
29
|
+
return row.size;
|
|
30
|
+
},
|
|
31
|
+
},
|
|
32
|
+
],
|
|
33
|
+
});
|
|
34
|
+
expect(lines.join('\n')).toMatchInlineSnapshot(`
|
|
35
|
+
" id app size
|
|
36
|
+
--- ------------- ---------
|
|
37
|
+
2 Zed 1720x1440
|
|
38
|
+
102 Google Chrome 3440x1440"
|
|
39
|
+
`);
|
|
40
|
+
});
|
|
41
|
+
});
|
package/dist/types.d.ts
CHANGED
|
@@ -10,8 +10,17 @@ export type Region = {
|
|
|
10
10
|
width: number;
|
|
11
11
|
height: number;
|
|
12
12
|
};
|
|
13
|
+
export type CoordMap = {
|
|
14
|
+
captureX: number;
|
|
15
|
+
captureY: number;
|
|
16
|
+
captureWidth: number;
|
|
17
|
+
captureHeight: number;
|
|
18
|
+
imageWidth: number;
|
|
19
|
+
imageHeight: number;
|
|
20
|
+
};
|
|
13
21
|
export type DisplayInfo = {
|
|
14
22
|
id: number;
|
|
23
|
+
index: number;
|
|
15
24
|
name: string;
|
|
16
25
|
x: number;
|
|
17
26
|
y: number;
|
|
@@ -20,14 +29,35 @@ export type DisplayInfo = {
|
|
|
20
29
|
scale: number;
|
|
21
30
|
isPrimary: boolean;
|
|
22
31
|
};
|
|
32
|
+
export type WindowInfo = {
|
|
33
|
+
id: number;
|
|
34
|
+
ownerPid: number;
|
|
35
|
+
ownerName: string;
|
|
36
|
+
title: string;
|
|
37
|
+
x: number;
|
|
38
|
+
y: number;
|
|
39
|
+
width: number;
|
|
40
|
+
height: number;
|
|
41
|
+
desktopIndex: number;
|
|
42
|
+
};
|
|
23
43
|
export type ScreenshotInput = {
|
|
24
44
|
path?: string;
|
|
25
45
|
display?: number;
|
|
46
|
+
window?: number;
|
|
26
47
|
region?: Region;
|
|
27
48
|
annotate?: boolean;
|
|
28
49
|
};
|
|
29
50
|
export type ScreenshotResult = {
|
|
30
51
|
path: string;
|
|
52
|
+
desktopIndex: number;
|
|
53
|
+
captureX: number;
|
|
54
|
+
captureY: number;
|
|
55
|
+
captureWidth: number;
|
|
56
|
+
captureHeight: number;
|
|
57
|
+
imageWidth: number;
|
|
58
|
+
imageHeight: number;
|
|
59
|
+
coordMap: string;
|
|
60
|
+
hint: string;
|
|
31
61
|
};
|
|
32
62
|
export type ClickInput = {
|
|
33
63
|
point: Point;
|
|
@@ -55,6 +85,20 @@ export type DragInput = {
|
|
|
55
85
|
durationMs?: number;
|
|
56
86
|
button: MouseButton;
|
|
57
87
|
};
|
|
88
|
+
export type NativeErrorObject = {
|
|
89
|
+
code: string;
|
|
90
|
+
message: string;
|
|
91
|
+
command: string;
|
|
92
|
+
};
|
|
93
|
+
export type NativeCommandResult = {
|
|
94
|
+
ok: boolean;
|
|
95
|
+
error?: NativeErrorObject;
|
|
96
|
+
};
|
|
97
|
+
export type NativeDataResult<T> = {
|
|
98
|
+
ok: boolean;
|
|
99
|
+
data?: T;
|
|
100
|
+
error?: NativeErrorObject;
|
|
101
|
+
};
|
|
58
102
|
export interface UseComputerBridge {
|
|
59
103
|
screenshot(input: ScreenshotInput): Promise<ScreenshotResult>;
|
|
60
104
|
click(input: ClickInput): Promise<void>;
|
|
@@ -72,6 +116,7 @@ export interface UseComputerBridge {
|
|
|
72
116
|
}): Promise<void>;
|
|
73
117
|
mousePosition(): Promise<Point>;
|
|
74
118
|
displayList(): Promise<DisplayInfo[]>;
|
|
119
|
+
windowList(): Promise<WindowInfo[]>;
|
|
75
120
|
clipboardGet(): Promise<string>;
|
|
76
121
|
clipboardSet(input: {
|
|
77
122
|
text: string;
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "usecomputer",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.4",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Fast computer automation CLI for AI agents. Control any desktop with accessibility snapshots, clicks, typing, scrolling, and more.",
|
|
6
6
|
"bin": "./bin.js",
|
|
@@ -12,6 +12,14 @@
|
|
|
12
12
|
"types": "./dist/index.d.ts",
|
|
13
13
|
"default": "./dist/index.js"
|
|
14
14
|
},
|
|
15
|
+
"./lib": {
|
|
16
|
+
"types": "./dist/lib.d.ts",
|
|
17
|
+
"default": "./dist/lib.js"
|
|
18
|
+
},
|
|
19
|
+
"./coord-map": {
|
|
20
|
+
"types": "./dist/coord-map.d.ts",
|
|
21
|
+
"default": "./dist/coord-map.js"
|
|
22
|
+
},
|
|
15
23
|
"./src": {
|
|
16
24
|
"types": "./src/index.ts",
|
|
17
25
|
"default": "./src/index.ts"
|
|
@@ -47,18 +55,19 @@
|
|
|
47
55
|
"license": "MIT",
|
|
48
56
|
"repository": {
|
|
49
57
|
"type": "git",
|
|
50
|
-
"url": "git+https://github.com/remorses/
|
|
58
|
+
"url": "git+https://github.com/remorses/kimaki.git",
|
|
51
59
|
"directory": "usecomputer"
|
|
52
60
|
},
|
|
53
|
-
"homepage": "https://github.com/remorses/
|
|
61
|
+
"homepage": "https://github.com/remorses/kimaki/tree/main/usecomputer",
|
|
54
62
|
"bugs": {
|
|
55
|
-
"url": "https://github.com/remorses/
|
|
63
|
+
"url": "https://github.com/remorses/kimaki/issues"
|
|
56
64
|
},
|
|
57
65
|
"os": [
|
|
58
66
|
"darwin"
|
|
59
67
|
],
|
|
60
68
|
"dependencies": {
|
|
61
69
|
"goke": "^6.3.0",
|
|
70
|
+
"picocolors": "^1.1.1",
|
|
62
71
|
"string-dedent": "^3.0.1",
|
|
63
72
|
"zod": "^4.3.6"
|
|
64
73
|
},
|
|
@@ -68,6 +77,9 @@
|
|
|
68
77
|
"typescript": "^5.8.3",
|
|
69
78
|
"vitest": "^4.0.18"
|
|
70
79
|
},
|
|
80
|
+
"optionalDependencies": {
|
|
81
|
+
"sharp": "^0.34.5"
|
|
82
|
+
},
|
|
71
83
|
"scripts": {
|
|
72
84
|
"build": "tsc && chmod +x bin.js",
|
|
73
85
|
"build:zig": "zig build",
|
|
@@ -1,85 +1,156 @@
|
|
|
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
|
|
|
3
4
|
import { describe, expect, test } from 'vitest'
|
|
4
5
|
import { createBridgeFromNative } from './bridge.js'
|
|
5
|
-
import
|
|
6
|
+
import { native } from './native-lib.js'
|
|
6
7
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
8
|
+
describe('native bridge contract', () => {
|
|
9
|
+
test('bridge calls hit real Zig module', async () => {
|
|
10
|
+
expect(native).toBeTruthy()
|
|
11
|
+
if (!native) {
|
|
12
|
+
return
|
|
13
|
+
}
|
|
11
14
|
|
|
12
|
-
|
|
13
|
-
return {
|
|
14
|
-
execute(command: string, payloadJson: string): string {
|
|
15
|
-
const payload = JSON.parse(payloadJson) as unknown
|
|
16
|
-
calls.push({ command, payload })
|
|
17
|
-
if (command === 'mouse-position') {
|
|
18
|
-
return JSON.stringify({ ok: true, data: { x: 10, y: 20 } })
|
|
19
|
-
}
|
|
20
|
-
if (command === 'display-list') {
|
|
21
|
-
return JSON.stringify({
|
|
22
|
-
ok: true,
|
|
23
|
-
data: [
|
|
24
|
-
{ id: 1, name: 'Built-in', x: 0, y: 0, width: 1512, height: 982, scale: 2, isPrimary: true },
|
|
25
|
-
],
|
|
26
|
-
})
|
|
27
|
-
}
|
|
28
|
-
if (command === 'clipboard-get') {
|
|
29
|
-
return JSON.stringify({ ok: true, data: { text: 'hello' } })
|
|
30
|
-
}
|
|
31
|
-
if (command === 'screenshot') {
|
|
32
|
-
return JSON.stringify({ ok: true, data: { path: '/tmp/test.png' } })
|
|
33
|
-
}
|
|
34
|
-
return JSON.stringify({ ok: true, data: null })
|
|
35
|
-
},
|
|
36
|
-
}
|
|
37
|
-
}
|
|
15
|
+
const bridge = createBridgeFromNative({ nativeModule: native })
|
|
38
16
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
17
|
+
const safeTarget = {
|
|
18
|
+
x: 0,
|
|
19
|
+
y: 0,
|
|
20
|
+
}
|
|
43
21
|
|
|
44
22
|
await bridge.click({
|
|
45
|
-
point:
|
|
23
|
+
point: safeTarget,
|
|
46
24
|
button: 'left',
|
|
47
|
-
count:
|
|
48
|
-
modifiers: [
|
|
25
|
+
count: 1,
|
|
26
|
+
modifiers: [],
|
|
49
27
|
})
|
|
50
|
-
await bridge.
|
|
51
|
-
await bridge.
|
|
52
|
-
await bridge.scroll({ direction: 'down', amount: 300 })
|
|
53
|
-
await bridge.drag({ from: { x: 10, y: 10 }, to: { x: 200, y: 120 }, button: 'left' })
|
|
54
|
-
await bridge.hover({ x: 33, y: 44 })
|
|
55
|
-
await bridge.mouseMove({ x: 60, y: 70 })
|
|
28
|
+
await bridge.hover(safeTarget)
|
|
29
|
+
await bridge.mouseMove(safeTarget)
|
|
56
30
|
await bridge.mouseDown({ button: 'left' })
|
|
57
31
|
await bridge.mouseUp({ button: 'left' })
|
|
58
|
-
await bridge.
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
32
|
+
await bridge.drag({
|
|
33
|
+
from: safeTarget,
|
|
34
|
+
to: { x: safeTarget.x + 6, y: safeTarget.y + 6 },
|
|
35
|
+
button: 'left',
|
|
36
|
+
durationMs: 10,
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
const screenshot = await bridge.screenshot({ path: `${process.cwd()}/tmp/bridge-contract-shot.png` })
|
|
40
|
+
|
|
41
|
+
await bridge.typeText({ text: 'h', delayMs: 30 })
|
|
42
|
+
await bridge.press({ key: 'backspace', count: 1 })
|
|
43
|
+
const scrollResult = await bridge.scroll({ direction: 'down', amount: 1 }).then(
|
|
44
|
+
() => {
|
|
45
|
+
return 'ok'
|
|
46
|
+
},
|
|
47
|
+
(error: unknown) => {
|
|
48
|
+
return error instanceof Error ? error.message : String(error)
|
|
49
|
+
},
|
|
50
|
+
)
|
|
51
|
+
const scrollAtResult = await bridge.scroll({ direction: 'right', amount: 1, at: safeTarget }).then(
|
|
52
|
+
() => {
|
|
53
|
+
return 'ok'
|
|
54
|
+
},
|
|
55
|
+
(error: unknown) => {
|
|
56
|
+
return error instanceof Error ? error.message : String(error)
|
|
57
|
+
},
|
|
58
|
+
)
|
|
59
|
+
const displays = await bridge.displayList()
|
|
60
|
+
const windows = await bridge.windowList()
|
|
61
|
+
const clipboardGetResult = await bridge.clipboardGet().then(
|
|
62
|
+
() => {
|
|
63
|
+
return 'ok'
|
|
64
|
+
},
|
|
65
|
+
(error: unknown) => {
|
|
66
|
+
return error instanceof Error ? error.message : String(error)
|
|
67
|
+
},
|
|
68
|
+
)
|
|
69
|
+
const clipboardSetResult = await bridge.clipboardSet({ text: 'copied' }).then(
|
|
70
|
+
() => {
|
|
71
|
+
return 'ok'
|
|
72
|
+
},
|
|
73
|
+
(error: unknown) => {
|
|
74
|
+
return error instanceof Error ? error.message : String(error)
|
|
75
|
+
},
|
|
76
|
+
)
|
|
77
|
+
const isOkOrTodo = ({ value }: { value: string }): boolean => {
|
|
78
|
+
return value === 'ok' || value.includes('TODO not implemented')
|
|
79
|
+
}
|
|
63
80
|
|
|
64
|
-
expect(
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
81
|
+
expect({
|
|
82
|
+
screenshotShape: {
|
|
83
|
+
path: screenshot.path,
|
|
84
|
+
desktopIndex: typeof screenshot.desktopIndex,
|
|
85
|
+
captureX: typeof screenshot.captureX,
|
|
86
|
+
captureY: typeof screenshot.captureY,
|
|
87
|
+
captureWidth: screenshot.captureWidth > 0,
|
|
88
|
+
captureHeight: screenshot.captureHeight > 0,
|
|
89
|
+
imageWidth: screenshot.imageWidth > 0,
|
|
90
|
+
imageHeight: screenshot.imageHeight > 0,
|
|
91
|
+
coordMapHasSixValues: screenshot.coordMap.split(',').length === 6,
|
|
92
|
+
hint: screenshot.hint,
|
|
93
|
+
},
|
|
94
|
+
firstDisplayShape: displays[0]
|
|
95
|
+
? {
|
|
96
|
+
id: typeof displays[0].id,
|
|
97
|
+
index: typeof displays[0].index,
|
|
98
|
+
width: displays[0].width > 0,
|
|
99
|
+
height: displays[0].height > 0,
|
|
100
|
+
}
|
|
101
|
+
: null,
|
|
102
|
+
firstWindowShape: windows[0]
|
|
103
|
+
? {
|
|
104
|
+
id: typeof windows[0].id,
|
|
105
|
+
ownerName: typeof windows[0].ownerName,
|
|
106
|
+
desktopIndex: typeof windows[0].desktopIndex,
|
|
107
|
+
}
|
|
108
|
+
: null,
|
|
109
|
+
optionalCommandOutcomes: {
|
|
110
|
+
scrollResult: isOkOrTodo({ value: scrollResult }),
|
|
111
|
+
scrollAtResult: isOkOrTodo({ value: scrollAtResult }),
|
|
112
|
+
clipboardGetResult: isOkOrTodo({ value: clipboardGetResult }),
|
|
113
|
+
clipboardSetResult: isOkOrTodo({ value: clipboardSetResult }),
|
|
114
|
+
},
|
|
115
|
+
}).toMatchInlineSnapshot(`
|
|
116
|
+
{
|
|
117
|
+
"firstDisplayShape": {
|
|
118
|
+
"height": true,
|
|
119
|
+
"id": "number",
|
|
120
|
+
"index": "number",
|
|
121
|
+
"width": true,
|
|
122
|
+
},
|
|
123
|
+
"firstWindowShape": {
|
|
124
|
+
"desktopIndex": "number",
|
|
125
|
+
"id": "number",
|
|
126
|
+
"ownerName": "string",
|
|
127
|
+
},
|
|
128
|
+
"optionalCommandOutcomes": {
|
|
129
|
+
"clipboardGetResult": true,
|
|
130
|
+
"clipboardSetResult": true,
|
|
131
|
+
"scrollAtResult": true,
|
|
132
|
+
"scrollResult": true,
|
|
133
|
+
},
|
|
134
|
+
"screenshotShape": {
|
|
135
|
+
"captureHeight": true,
|
|
136
|
+
"captureWidth": true,
|
|
137
|
+
"captureX": "number",
|
|
138
|
+
"captureY": "number",
|
|
139
|
+
"coordMapHasSixValues": true,
|
|
140
|
+
"desktopIndex": "number",
|
|
141
|
+
"hint": "ALWAYS pass this exact coord map to click, hover, drag, and mouse move when using coordinates from this screenshot:
|
|
142
|
+
--coord-map "0,0,3440,1440,1568,656"
|
|
143
|
+
|
|
144
|
+
Example:
|
|
145
|
+
usecomputer click -x 400 -y 220 --coord-map "0,0,3440,1440,1568,656"",
|
|
146
|
+
"imageHeight": true,
|
|
147
|
+
"imageWidth": true,
|
|
148
|
+
"path": "/Users/morse/Documents/GitHub/kimakivoice/usecomputer/tmp/bridge-contract-shot.png",
|
|
149
|
+
},
|
|
150
|
+
}
|
|
83
151
|
`)
|
|
152
|
+
|
|
153
|
+
expect(displays.length).toBeGreaterThan(0)
|
|
154
|
+
expect(windows.length).toBeGreaterThan(0)
|
|
84
155
|
})
|
|
85
156
|
})
|