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
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { Point, Region, ScrollDirection } from './types.js';
|
|
2
|
+
export declare function parsePoint(input: string): Error | Point;
|
|
3
|
+
export declare function parseRegion(input: string): Error | Region;
|
|
4
|
+
export declare function parseModifiers(input?: string): string[];
|
|
5
|
+
export declare function parseDirection(input: string): Error | ScrollDirection;
|
|
6
|
+
//# sourceMappingURL=command-parsers.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"command-parsers.d.ts","sourceRoot":"","sources":["../src/command-parsers.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,eAAe,EAAE,MAAM,YAAY,CAAA;AAEhE,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,KAAK,GAAG,KAAK,CAavD;AAED,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,KAAK,GAAG,MAAM,CAkBzD;AAED,wBAAgB,cAAc,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAYvD;AAED,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,KAAK,GAAG,eAAe,CAMrE"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
// Parser helpers for CLI values such as coordinates, regions, and key modifiers.
|
|
2
|
+
export function parsePoint(input) {
|
|
3
|
+
const parts = input.split(',').map((value) => {
|
|
4
|
+
return value.trim();
|
|
5
|
+
});
|
|
6
|
+
if (parts.length !== 2) {
|
|
7
|
+
return new Error(`Invalid point \"${input}\". Expected x,y`);
|
|
8
|
+
}
|
|
9
|
+
const x = Number(parts[0]);
|
|
10
|
+
const y = Number(parts[1]);
|
|
11
|
+
if (!Number.isFinite(x) || !Number.isFinite(y)) {
|
|
12
|
+
return new Error(`Invalid point \"${input}\". Coordinates must be numbers`);
|
|
13
|
+
}
|
|
14
|
+
return { x, y };
|
|
15
|
+
}
|
|
16
|
+
export function parseRegion(input) {
|
|
17
|
+
const parts = input.split(',').map((value) => {
|
|
18
|
+
return value.trim();
|
|
19
|
+
});
|
|
20
|
+
if (parts.length !== 4) {
|
|
21
|
+
return new Error(`Invalid region \"${input}\". Expected x,y,width,height`);
|
|
22
|
+
}
|
|
23
|
+
const x = Number(parts[0]);
|
|
24
|
+
const y = Number(parts[1]);
|
|
25
|
+
const width = Number(parts[2]);
|
|
26
|
+
const height = Number(parts[3]);
|
|
27
|
+
if (!Number.isFinite(x) || !Number.isFinite(y) || !Number.isFinite(width) || !Number.isFinite(height)) {
|
|
28
|
+
return new Error(`Invalid region \"${input}\". Values must be numbers`);
|
|
29
|
+
}
|
|
30
|
+
if (width <= 0 || height <= 0) {
|
|
31
|
+
return new Error(`Invalid region \"${input}\". Width and height must be greater than 0`);
|
|
32
|
+
}
|
|
33
|
+
return { x, y, width, height };
|
|
34
|
+
}
|
|
35
|
+
export function parseModifiers(input) {
|
|
36
|
+
if (!input) {
|
|
37
|
+
return [];
|
|
38
|
+
}
|
|
39
|
+
return input
|
|
40
|
+
.split(',')
|
|
41
|
+
.map((value) => {
|
|
42
|
+
return value.trim().toLowerCase();
|
|
43
|
+
})
|
|
44
|
+
.filter((value) => {
|
|
45
|
+
return value.length > 0;
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
export function parseDirection(input) {
|
|
49
|
+
const normalized = input.trim().toLowerCase();
|
|
50
|
+
if (normalized === 'up' || normalized === 'down' || normalized === 'left' || normalized === 'right') {
|
|
51
|
+
return normalized;
|
|
52
|
+
}
|
|
53
|
+
return new Error(`Invalid direction \"${input}\". Expected up, down, left, or right`);
|
|
54
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"command-parsers.test.d.ts","sourceRoot":"","sources":["../src/command-parsers.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
// Tests for parsing coordinates, regions, directions, and keyboard modifiers.
|
|
2
|
+
import { describe, expect, test } from 'vitest';
|
|
3
|
+
import { parseDirection, parseModifiers, parsePoint, parseRegion } from './command-parsers.js';
|
|
4
|
+
describe('command parsers', () => {
|
|
5
|
+
test('parses x,y points', () => {
|
|
6
|
+
const result = parsePoint('100,200');
|
|
7
|
+
expect(result).toMatchInlineSnapshot(`
|
|
8
|
+
{
|
|
9
|
+
"x": 100,
|
|
10
|
+
"y": 200,
|
|
11
|
+
}
|
|
12
|
+
`);
|
|
13
|
+
});
|
|
14
|
+
test('rejects invalid points', () => {
|
|
15
|
+
const result = parsePoint('100');
|
|
16
|
+
expect(result instanceof Error).toBe(true);
|
|
17
|
+
expect(result instanceof Error ? result.message : '').toMatchInlineSnapshot(`"Invalid point "100". Expected x,y"`);
|
|
18
|
+
});
|
|
19
|
+
test('parses x,y,width,height regions', () => {
|
|
20
|
+
const result = parseRegion('10,20,300,400');
|
|
21
|
+
expect(result).toMatchInlineSnapshot(`
|
|
22
|
+
{
|
|
23
|
+
"height": 400,
|
|
24
|
+
"width": 300,
|
|
25
|
+
"x": 10,
|
|
26
|
+
"y": 20,
|
|
27
|
+
}
|
|
28
|
+
`);
|
|
29
|
+
});
|
|
30
|
+
test('parses modifiers with normalization', () => {
|
|
31
|
+
expect(parseModifiers(' CMD,shift, alt ')).toMatchInlineSnapshot(`
|
|
32
|
+
[
|
|
33
|
+
"cmd",
|
|
34
|
+
"shift",
|
|
35
|
+
"alt",
|
|
36
|
+
]
|
|
37
|
+
`);
|
|
38
|
+
});
|
|
39
|
+
test('validates scroll direction', () => {
|
|
40
|
+
expect(parseDirection('down')).toBe('down');
|
|
41
|
+
const invalid = parseDirection('diagonal');
|
|
42
|
+
expect(invalid instanceof Error).toBe(true);
|
|
43
|
+
});
|
|
44
|
+
});
|
|
Binary file
|
|
Binary file
|
package/dist/index.d.ts
CHANGED
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,SAAS,EAAE,MAAM,UAAU,CAAA;AACpC,OAAO,EAAE,YAAY,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAA;AAClE,cAAc,YAAY,CAAA;AAC1B,cAAc,sBAAsB,CAAA"}
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
//
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
export
|
|
1
|
+
// Public API exports for usecomputer parser, bridge, and CLI modules.
|
|
2
|
+
export { createCli } from './cli.js';
|
|
3
|
+
export { createBridge, createBridgeFromNative } from './bridge.js';
|
|
4
|
+
export * from './types.js';
|
|
5
|
+
export * from './command-parsers.js';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"native-click-smoke.test.d.ts","sourceRoot":"","sources":["../src/native-click-smoke.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
// Optional host smoke test for the real native click command.
|
|
2
|
+
import { describe, expect, test } from 'vitest';
|
|
3
|
+
import { native } from './native-lib.js';
|
|
4
|
+
const runNativeSmoke = process.env.USECOMPUTER_NATIVE_SMOKE === '1';
|
|
5
|
+
describe('native click smoke', () => {
|
|
6
|
+
const smokeTest = runNativeSmoke ? test : test.skip;
|
|
7
|
+
smokeTest('executes click command without crashing', () => {
|
|
8
|
+
expect(native).toBeTruthy();
|
|
9
|
+
if (!native) {
|
|
10
|
+
return;
|
|
11
|
+
}
|
|
12
|
+
const response = native.execute('click', JSON.stringify({
|
|
13
|
+
point: { x: 10, y: 10 },
|
|
14
|
+
button: 'left',
|
|
15
|
+
count: 1,
|
|
16
|
+
}));
|
|
17
|
+
const parsed = JSON.parse(response);
|
|
18
|
+
expect(parsed).toMatchInlineSnapshot(`
|
|
19
|
+
{
|
|
20
|
+
"data": null,
|
|
21
|
+
"ok": true,
|
|
22
|
+
}
|
|
23
|
+
`);
|
|
24
|
+
expect(typeof parsed.ok).toBe('boolean');
|
|
25
|
+
});
|
|
26
|
+
smokeTest('executes mouse-move/down/up/position/hover/drag without crashing', () => {
|
|
27
|
+
expect(native).toBeTruthy();
|
|
28
|
+
if (!native) {
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
const moveResponse = JSON.parse(native.execute('mouse-move', JSON.stringify({
|
|
32
|
+
x: 20,
|
|
33
|
+
y: 20,
|
|
34
|
+
})));
|
|
35
|
+
const downResponse = JSON.parse(native.execute('mouse-down', JSON.stringify({ button: 'left' })));
|
|
36
|
+
const upResponse = JSON.parse(native.execute('mouse-up', JSON.stringify({ button: 'left' })));
|
|
37
|
+
const positionResponse = JSON.parse(native.execute('mouse-position', JSON.stringify({})));
|
|
38
|
+
const hoverResponse = JSON.parse(native.execute('hover', JSON.stringify({
|
|
39
|
+
x: 24,
|
|
40
|
+
y: 24,
|
|
41
|
+
})));
|
|
42
|
+
const dragResponse = JSON.parse(native.execute('drag', JSON.stringify({
|
|
43
|
+
from: { x: 24, y: 24 },
|
|
44
|
+
to: { x: 30, y: 30 },
|
|
45
|
+
button: 'left',
|
|
46
|
+
durationMs: 10,
|
|
47
|
+
})));
|
|
48
|
+
expect({
|
|
49
|
+
moveResponse,
|
|
50
|
+
downResponse,
|
|
51
|
+
upResponse,
|
|
52
|
+
positionResponse,
|
|
53
|
+
hoverResponse,
|
|
54
|
+
dragResponse,
|
|
55
|
+
}).toMatchInlineSnapshot(`
|
|
56
|
+
{
|
|
57
|
+
"downResponse": {
|
|
58
|
+
"data": null,
|
|
59
|
+
"ok": true,
|
|
60
|
+
},
|
|
61
|
+
"dragResponse": {
|
|
62
|
+
"data": null,
|
|
63
|
+
"ok": true,
|
|
64
|
+
},
|
|
65
|
+
"hoverResponse": {
|
|
66
|
+
"data": null,
|
|
67
|
+
"ok": true,
|
|
68
|
+
},
|
|
69
|
+
"moveResponse": {
|
|
70
|
+
"data": null,
|
|
71
|
+
"ok": true,
|
|
72
|
+
},
|
|
73
|
+
"positionResponse": {
|
|
74
|
+
"data": {
|
|
75
|
+
"x": 20,
|
|
76
|
+
"y": 20,
|
|
77
|
+
},
|
|
78
|
+
"ok": true,
|
|
79
|
+
},
|
|
80
|
+
"upResponse": {
|
|
81
|
+
"data": null,
|
|
82
|
+
"ok": true,
|
|
83
|
+
},
|
|
84
|
+
}
|
|
85
|
+
`);
|
|
86
|
+
expect(moveResponse.ok).toBe(true);
|
|
87
|
+
expect(downResponse.ok).toBe(true);
|
|
88
|
+
expect(upResponse.ok).toBe(true);
|
|
89
|
+
expect(positionResponse.ok).toBe(true);
|
|
90
|
+
expect(hoverResponse.ok).toBe(true);
|
|
91
|
+
expect(dragResponse.ok).toBe(true);
|
|
92
|
+
});
|
|
93
|
+
});
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
// Loads the platform-specific usecomputer native addon built from Zig.
|
|
2
|
+
|
|
3
|
+
const os = require('node:os')
|
|
4
|
+
|
|
5
|
+
function loadCandidate(path) {
|
|
6
|
+
try {
|
|
7
|
+
return require(path)
|
|
8
|
+
} catch {
|
|
9
|
+
return undefined
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function loadNativeModule() {
|
|
14
|
+
const dev = loadCandidate('../zig-out/lib/usecomputer.node')
|
|
15
|
+
if (dev) {
|
|
16
|
+
return dev
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const platform = os.platform()
|
|
20
|
+
const arch = os.arch()
|
|
21
|
+
const target = `${platform}-${arch}`
|
|
22
|
+
|
|
23
|
+
const packaged = loadCandidate(`../dist/${target}/usecomputer.node`)
|
|
24
|
+
if (packaged) {
|
|
25
|
+
return packaged
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
return null
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const native = loadNativeModule()
|
|
32
|
+
|
|
33
|
+
module.exports = { native }
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"native-lib.d.ts","sourceRoot":"","sources":["../src/native-lib.ts"],"names":[],"mappings":"AAOA,MAAM,WAAW,YAAY;IAC3B,OAAO,CAAC,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,MAAM,CAAA;CACtD;AA4BD,eAAO,MAAM,MAAM,qBAAqB,CAAA"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
// ESM native loader for the usecomputer Zig addon using createRequire.
|
|
2
|
+
import os from 'node:os';
|
|
3
|
+
import { createRequire } from 'node:module';
|
|
4
|
+
const require = createRequire(import.meta.url);
|
|
5
|
+
function loadCandidate(path) {
|
|
6
|
+
try {
|
|
7
|
+
return require(path);
|
|
8
|
+
}
|
|
9
|
+
catch {
|
|
10
|
+
return null;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
function loadNativeModule() {
|
|
14
|
+
const dev = loadCandidate('../zig-out/lib/usecomputer.node');
|
|
15
|
+
if (dev) {
|
|
16
|
+
return dev;
|
|
17
|
+
}
|
|
18
|
+
const platform = os.platform();
|
|
19
|
+
const arch = os.arch();
|
|
20
|
+
const target = `${platform}-${arch}`;
|
|
21
|
+
const packaged = loadCandidate(`../dist/${target}/usecomputer.node`);
|
|
22
|
+
if (packaged) {
|
|
23
|
+
return packaged;
|
|
24
|
+
}
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
27
|
+
export const native = loadNativeModule();
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
export type MouseButton = 'left' | 'right' | 'middle';
|
|
2
|
+
export type ScrollDirection = 'up' | 'down' | 'left' | 'right';
|
|
3
|
+
export type Point = {
|
|
4
|
+
x: number;
|
|
5
|
+
y: number;
|
|
6
|
+
};
|
|
7
|
+
export type Region = {
|
|
8
|
+
x: number;
|
|
9
|
+
y: number;
|
|
10
|
+
width: number;
|
|
11
|
+
height: number;
|
|
12
|
+
};
|
|
13
|
+
export type DisplayInfo = {
|
|
14
|
+
id: number;
|
|
15
|
+
name: string;
|
|
16
|
+
x: number;
|
|
17
|
+
y: number;
|
|
18
|
+
width: number;
|
|
19
|
+
height: number;
|
|
20
|
+
scale: number;
|
|
21
|
+
isPrimary: boolean;
|
|
22
|
+
};
|
|
23
|
+
export type ScreenshotInput = {
|
|
24
|
+
path?: string;
|
|
25
|
+
display?: number;
|
|
26
|
+
region?: Region;
|
|
27
|
+
annotate?: boolean;
|
|
28
|
+
};
|
|
29
|
+
export type ScreenshotResult = {
|
|
30
|
+
path: string;
|
|
31
|
+
};
|
|
32
|
+
export type ClickInput = {
|
|
33
|
+
point: Point;
|
|
34
|
+
button: MouseButton;
|
|
35
|
+
count: number;
|
|
36
|
+
modifiers: string[];
|
|
37
|
+
};
|
|
38
|
+
export type TypeInput = {
|
|
39
|
+
text: string;
|
|
40
|
+
delayMs?: number;
|
|
41
|
+
};
|
|
42
|
+
export type PressInput = {
|
|
43
|
+
key: string;
|
|
44
|
+
count: number;
|
|
45
|
+
delayMs?: number;
|
|
46
|
+
};
|
|
47
|
+
export type ScrollInput = {
|
|
48
|
+
direction: ScrollDirection;
|
|
49
|
+
amount: number;
|
|
50
|
+
at?: Point;
|
|
51
|
+
};
|
|
52
|
+
export type DragInput = {
|
|
53
|
+
from: Point;
|
|
54
|
+
to: Point;
|
|
55
|
+
durationMs?: number;
|
|
56
|
+
button: MouseButton;
|
|
57
|
+
};
|
|
58
|
+
export interface UseComputerBridge {
|
|
59
|
+
screenshot(input: ScreenshotInput): Promise<ScreenshotResult>;
|
|
60
|
+
click(input: ClickInput): Promise<void>;
|
|
61
|
+
typeText(input: TypeInput): Promise<void>;
|
|
62
|
+
press(input: PressInput): Promise<void>;
|
|
63
|
+
scroll(input: ScrollInput): Promise<void>;
|
|
64
|
+
drag(input: DragInput): Promise<void>;
|
|
65
|
+
hover(input: Point): Promise<void>;
|
|
66
|
+
mouseMove(input: Point): Promise<void>;
|
|
67
|
+
mouseDown(input: {
|
|
68
|
+
button: MouseButton;
|
|
69
|
+
}): Promise<void>;
|
|
70
|
+
mouseUp(input: {
|
|
71
|
+
button: MouseButton;
|
|
72
|
+
}): Promise<void>;
|
|
73
|
+
mousePosition(): Promise<Point>;
|
|
74
|
+
displayList(): Promise<DisplayInfo[]>;
|
|
75
|
+
clipboardGet(): Promise<string>;
|
|
76
|
+
clipboardSet(input: {
|
|
77
|
+
text: string;
|
|
78
|
+
}): Promise<void>;
|
|
79
|
+
}
|
|
80
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
package/dist/types.js
ADDED
package/package.json
CHANGED
|
@@ -1,11 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "usecomputer",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.3",
|
|
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
|
-
"bin":
|
|
7
|
-
"usecomputer": "./bin.js"
|
|
8
|
-
},
|
|
6
|
+
"bin": "./bin.js",
|
|
9
7
|
"main": "./dist/index.js",
|
|
10
8
|
"types": "./dist/index.d.ts",
|
|
11
9
|
"exports": {
|
|
@@ -26,8 +24,12 @@
|
|
|
26
24
|
"files": [
|
|
27
25
|
"src",
|
|
28
26
|
"dist",
|
|
27
|
+
"zig",
|
|
28
|
+
"build.zig",
|
|
29
|
+
"build.zig.zon",
|
|
29
30
|
"bin.js",
|
|
30
|
-
"README.md"
|
|
31
|
+
"README.md",
|
|
32
|
+
"CHANGELOG.md"
|
|
31
33
|
],
|
|
32
34
|
"keywords": [
|
|
33
35
|
"computer-use",
|
|
@@ -45,24 +47,33 @@
|
|
|
45
47
|
"license": "MIT",
|
|
46
48
|
"repository": {
|
|
47
49
|
"type": "git",
|
|
48
|
-
"url": "https://github.com/remorses/
|
|
49
|
-
"directory": "
|
|
50
|
+
"url": "git+https://github.com/remorses/kimakivoice.git",
|
|
51
|
+
"directory": "usecomputer"
|
|
50
52
|
},
|
|
51
|
-
"homepage": "https://github.com/remorses/usecomputer",
|
|
53
|
+
"homepage": "https://github.com/remorses/kimakivoice/tree/main/usecomputer",
|
|
52
54
|
"bugs": {
|
|
53
|
-
"url": "https://github.com/remorses/
|
|
55
|
+
"url": "https://github.com/remorses/kimakivoice/issues"
|
|
54
56
|
},
|
|
57
|
+
"os": [
|
|
58
|
+
"darwin"
|
|
59
|
+
],
|
|
55
60
|
"dependencies": {
|
|
56
|
-
"goke": "^6.
|
|
61
|
+
"goke": "^6.3.0",
|
|
57
62
|
"string-dedent": "^3.0.1",
|
|
58
63
|
"zod": "^4.3.6"
|
|
59
64
|
},
|
|
60
65
|
"devDependencies": {
|
|
61
66
|
"@types/node": "^22.15.3",
|
|
62
|
-
"
|
|
67
|
+
"tsx": "^4.21.0",
|
|
68
|
+
"typescript": "^5.8.3",
|
|
69
|
+
"vitest": "^4.0.18"
|
|
63
70
|
},
|
|
64
71
|
"scripts": {
|
|
65
|
-
"build": "tsc",
|
|
72
|
+
"build": "tsc && chmod +x bin.js",
|
|
73
|
+
"build:zig": "zig build",
|
|
74
|
+
"build:native": "tsx scripts/build.ts",
|
|
75
|
+
"build:native:macos": "tsx scripts/build.ts darwin-arm64 darwin-x64",
|
|
76
|
+
"test": "vitest --run",
|
|
66
77
|
"typecheck": "tsc --noEmit"
|
|
67
78
|
}
|
|
68
79
|
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
// Contract tests for command names and payloads emitted to the native bridge.
|
|
2
|
+
|
|
3
|
+
import { describe, expect, test } from 'vitest'
|
|
4
|
+
import { createBridgeFromNative } from './bridge.js'
|
|
5
|
+
import type { NativeModule } from './native-lib.js'
|
|
6
|
+
|
|
7
|
+
type Call = {
|
|
8
|
+
command: string
|
|
9
|
+
payload: unknown
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function createFakeNative({ calls }: { calls: Call[] }): NativeModule {
|
|
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
|
+
}
|
|
38
|
+
|
|
39
|
+
describe('native bridge contract', () => {
|
|
40
|
+
test('maps high-level calls to native commands', async () => {
|
|
41
|
+
const calls: Call[] = []
|
|
42
|
+
const bridge = createBridgeFromNative({ nativeModule: createFakeNative({ calls }) })
|
|
43
|
+
|
|
44
|
+
await bridge.click({
|
|
45
|
+
point: { x: 100, y: 200 },
|
|
46
|
+
button: 'left',
|
|
47
|
+
count: 2,
|
|
48
|
+
modifiers: ['cmd'],
|
|
49
|
+
})
|
|
50
|
+
await bridge.typeText({ text: 'hello', delayMs: 30 })
|
|
51
|
+
await bridge.press({ key: 'enter', count: 1 })
|
|
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 })
|
|
56
|
+
await bridge.mouseDown({ button: 'left' })
|
|
57
|
+
await bridge.mouseUp({ button: 'left' })
|
|
58
|
+
await bridge.mousePosition()
|
|
59
|
+
await bridge.displayList()
|
|
60
|
+
await bridge.clipboardGet()
|
|
61
|
+
await bridge.clipboardSet({ text: 'copied' })
|
|
62
|
+
await bridge.screenshot({ path: './out.png' })
|
|
63
|
+
|
|
64
|
+
expect(calls.map((call) => {
|
|
65
|
+
return call.command
|
|
66
|
+
})).toMatchInlineSnapshot(`
|
|
67
|
+
[
|
|
68
|
+
"click",
|
|
69
|
+
"type-text",
|
|
70
|
+
"press",
|
|
71
|
+
"scroll",
|
|
72
|
+
"drag",
|
|
73
|
+
"hover",
|
|
74
|
+
"mouse-move",
|
|
75
|
+
"mouse-down",
|
|
76
|
+
"mouse-up",
|
|
77
|
+
"mouse-position",
|
|
78
|
+
"display-list",
|
|
79
|
+
"clipboard-get",
|
|
80
|
+
"clipboard-set",
|
|
81
|
+
"screenshot",
|
|
82
|
+
]
|
|
83
|
+
`)
|
|
84
|
+
})
|
|
85
|
+
})
|