usecomputer 0.0.3 → 0.1.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/CHANGELOG.md +67 -0
- package/README.md +324 -0
- package/build.zig +95 -11
- package/build.zig.zon +5 -0
- package/dist/bridge-contract.test.js +61 -67
- 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 +19 -5
- package/src/bridge-contract.test.ts +68 -73
- 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 +1966 -270
- package/zig/src/main.zig +382 -0
- 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.1.0",
|
|
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,20 @@
|
|
|
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
|
-
"darwin"
|
|
66
|
+
"darwin",
|
|
67
|
+
"linux"
|
|
59
68
|
],
|
|
60
69
|
"dependencies": {
|
|
61
70
|
"goke": "^6.3.0",
|
|
71
|
+
"picocolors": "^1.1.1",
|
|
62
72
|
"string-dedent": "^3.0.1",
|
|
63
73
|
"zod": "^4.3.6"
|
|
64
74
|
},
|
|
@@ -68,11 +78,15 @@
|
|
|
68
78
|
"typescript": "^5.8.3",
|
|
69
79
|
"vitest": "^4.0.18"
|
|
70
80
|
},
|
|
81
|
+
"optionalDependencies": {
|
|
82
|
+
"sharp": "^0.34.5"
|
|
83
|
+
},
|
|
71
84
|
"scripts": {
|
|
72
85
|
"build": "tsc && chmod +x bin.js",
|
|
73
86
|
"build:zig": "zig build",
|
|
74
87
|
"build:native": "tsx scripts/build.ts",
|
|
75
88
|
"build:native:macos": "tsx scripts/build.ts darwin-arm64 darwin-x64",
|
|
89
|
+
"vm": "tsx scripts/vm.ts",
|
|
76
90
|
"test": "vitest --run",
|
|
77
91
|
"typecheck": "tsc --noEmit"
|
|
78
92
|
}
|
|
@@ -1,85 +1,80 @@
|
|
|
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
|
|
|
4
|
+
import fs from 'node:fs'
|
|
5
|
+
import os from 'node:os'
|
|
3
6
|
import { describe, expect, test } from 'vitest'
|
|
4
7
|
import { createBridgeFromNative } from './bridge.js'
|
|
5
|
-
import
|
|
8
|
+
import { native } from './native-lib.js'
|
|
6
9
|
|
|
7
|
-
|
|
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
|
-
}
|
|
10
|
+
const isMacOS = os.platform() === 'darwin'
|
|
38
11
|
|
|
39
12
|
describe('native bridge contract', () => {
|
|
40
|
-
test('
|
|
41
|
-
|
|
42
|
-
|
|
13
|
+
test('bridge calls hit real Zig module', async () => {
|
|
14
|
+
expect(native).toBeTruthy()
|
|
15
|
+
if (!native) {
|
|
16
|
+
return
|
|
17
|
+
}
|
|
43
18
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
})
|
|
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 })
|
|
19
|
+
const bridge = createBridgeFromNative({ nativeModule: native })
|
|
20
|
+
|
|
21
|
+
const safeTarget = { x: 0, y: 0 }
|
|
22
|
+
|
|
23
|
+
// -- Mouse commands --
|
|
24
|
+
await bridge.click({ point: safeTarget, button: 'left', count: 1, modifiers: [] })
|
|
25
|
+
await bridge.hover(safeTarget)
|
|
26
|
+
await bridge.mouseMove(safeTarget)
|
|
56
27
|
await bridge.mouseDown({ button: 'left' })
|
|
57
28
|
await bridge.mouseUp({ button: 'left' })
|
|
58
|
-
await bridge.
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
29
|
+
await bridge.drag({
|
|
30
|
+
from: safeTarget,
|
|
31
|
+
to: { x: safeTarget.x + 6, y: safeTarget.y + 6 },
|
|
32
|
+
button: 'left',
|
|
33
|
+
durationMs: 10,
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
// -- Screenshot --
|
|
37
|
+
const screenshotPath = `${process.cwd()}/tmp/bridge-contract-shot.png`
|
|
38
|
+
const shot = await bridge.screenshot({ path: screenshotPath })
|
|
39
|
+
expect(shot.captureWidth).toBeGreaterThan(0)
|
|
40
|
+
expect(shot.captureHeight).toBeGreaterThan(0)
|
|
41
|
+
expect(shot.imageWidth).toBeGreaterThan(0)
|
|
42
|
+
expect(shot.imageHeight).toBeGreaterThan(0)
|
|
43
|
+
expect(shot.coordMap.split(',').length).toBe(6)
|
|
44
|
+
expect(shot.hint).toContain('--coord-map')
|
|
45
|
+
expect(fs.existsSync(screenshotPath)).toBe(true)
|
|
46
|
+
const stat = fs.statSync(screenshotPath)
|
|
47
|
+
expect(stat.size).toBeGreaterThan(100)
|
|
48
|
+
|
|
49
|
+
// -- Keyboard (works on both platforms) --
|
|
50
|
+
await bridge.typeText({ text: 'h', delayMs: 30 })
|
|
51
|
+
await bridge.press({ key: 'backspace', count: 1 })
|
|
52
|
+
|
|
53
|
+
// -- Scroll --
|
|
54
|
+
await bridge.scroll({ direction: 'down', amount: 1 })
|
|
55
|
+
await bridge.scroll({ direction: 'right', amount: 1, at: safeTarget })
|
|
56
|
+
|
|
57
|
+
// -- Display list --
|
|
58
|
+
const displayList = await bridge.displayList()
|
|
59
|
+
expect(displayList.length).toBeGreaterThan(0)
|
|
60
|
+
const firstDisplay = displayList[0]!
|
|
61
|
+
expect(firstDisplay.width).toBeGreaterThan(0)
|
|
62
|
+
expect(firstDisplay.height).toBeGreaterThan(0)
|
|
63
|
+
expect(typeof firstDisplay.id).toBe('number')
|
|
64
|
+
expect(typeof firstDisplay.index).toBe('number')
|
|
65
|
+
|
|
66
|
+
// -- Window list --
|
|
67
|
+
if (isMacOS) {
|
|
68
|
+
const windowList = await bridge.windowList()
|
|
69
|
+
expect(windowList.length).toBeGreaterThan(0)
|
|
70
|
+
const firstWindow = windowList[0]!
|
|
71
|
+
expect(typeof firstWindow.id).toBe('number')
|
|
72
|
+
expect(typeof firstWindow.ownerName).toBe('string')
|
|
73
|
+
expect(typeof firstWindow.desktopIndex).toBe('number')
|
|
74
|
+
}
|
|
63
75
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
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
|
-
`)
|
|
76
|
+
// -- Clipboard (TODO on all platforms — Zig returns "TODO not implemented") --
|
|
77
|
+
await expect(bridge.clipboardSet({ text: 'bridge-contract-test' })).rejects.toThrow('TODO not implemented')
|
|
78
|
+
await expect(bridge.clipboardGet()).rejects.toThrow('TODO not implemented')
|
|
84
79
|
})
|
|
85
80
|
})
|