@amaster.ai/pi-computer-use 0.1.1-beta.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.
Files changed (42) hide show
  1. package/LICENSE +201 -0
  2. package/dist/__tests__/computer-client.test.d.ts +2 -0
  3. package/dist/__tests__/computer-client.test.d.ts.map +1 -0
  4. package/dist/__tests__/computer-client.test.js +174 -0
  5. package/dist/__tests__/computer-client.test.js.map +1 -0
  6. package/dist/__tests__/index.test.d.ts +2 -0
  7. package/dist/__tests__/index.test.d.ts.map +1 -0
  8. package/dist/__tests__/index.test.js +385 -0
  9. package/dist/__tests__/index.test.js.map +1 -0
  10. package/dist/__tests__/server-process.test.d.ts +2 -0
  11. package/dist/__tests__/server-process.test.d.ts.map +1 -0
  12. package/dist/__tests__/server-process.test.js +127 -0
  13. package/dist/__tests__/server-process.test.js.map +1 -0
  14. package/dist/__tests__/vision.test.d.ts +2 -0
  15. package/dist/__tests__/vision.test.d.ts.map +1 -0
  16. package/dist/__tests__/vision.test.js +36 -0
  17. package/dist/__tests__/vision.test.js.map +1 -0
  18. package/dist/actions.d.ts +15 -0
  19. package/dist/actions.d.ts.map +1 -0
  20. package/dist/actions.js +45 -0
  21. package/dist/actions.js.map +1 -0
  22. package/dist/computer-client.d.ts +13 -0
  23. package/dist/computer-client.d.ts.map +1 -0
  24. package/dist/computer-client.js +109 -0
  25. package/dist/computer-client.js.map +1 -0
  26. package/dist/config.d.ts +31 -0
  27. package/dist/config.d.ts.map +1 -0
  28. package/dist/config.js +25 -0
  29. package/dist/config.js.map +1 -0
  30. package/dist/index.d.ts +6 -0
  31. package/dist/index.d.ts.map +1 -0
  32. package/dist/index.js +102 -0
  33. package/dist/index.js.map +1 -0
  34. package/dist/server-process.d.ts +9 -0
  35. package/dist/server-process.d.ts.map +1 -0
  36. package/dist/server-process.js +76 -0
  37. package/dist/server-process.js.map +1 -0
  38. package/dist/vision.d.ts +6 -0
  39. package/dist/vision.d.ts.map +1 -0
  40. package/dist/vision.js +57 -0
  41. package/dist/vision.js.map +1 -0
  42. package/package.json +52 -0
@@ -0,0 +1,45 @@
1
+ export async function dispatchAction(client, action) {
2
+ switch (action.type) {
3
+ case 'screenshot':
4
+ return undefined;
5
+ case 'click': {
6
+ const cmd = action.button === 'right' ? 'right_click' : 'left_click';
7
+ await client.sendCommand(cmd, { x: action.x, y: action.y });
8
+ return `Clicked (${action.button ?? 'left'}) at (${action.x}, ${action.y})`;
9
+ }
10
+ case 'double_click':
11
+ await client.sendCommand('double_click', { x: action.x, y: action.y });
12
+ return `Double-clicked at (${action.x}, ${action.y})`;
13
+ case 'type':
14
+ await client.sendCommand('type_text', { text: action.text });
15
+ return `Typed "${action.text}"`;
16
+ case 'keypress':
17
+ await client.sendCommand('hotkey', { keys: action.keys });
18
+ return `Pressed keys: ${action.keys?.join('+')}`;
19
+ case 'scroll':
20
+ await client.sendCommand('scroll', {
21
+ x: action.scroll_x ?? 0,
22
+ y: action.scroll_y ?? 0,
23
+ });
24
+ return `Scrolled (${action.scroll_x ?? 0}, ${action.scroll_y ?? 0})`;
25
+ case 'move':
26
+ await client.sendCommand('move_cursor', { x: action.x, y: action.y });
27
+ return `Moved cursor to (${action.x}, ${action.y})`;
28
+ case 'drag':
29
+ await client.sendCommand('drag', { path: action.path, button: action.button ?? 'left' });
30
+ return `Dragged along ${action.path?.length ?? 0} points`;
31
+ case 'wait':
32
+ await new Promise((r) => setTimeout(r, 1_000));
33
+ return 'Waited 1 second';
34
+ case 'run_command': {
35
+ const result = await client.sendCommand('run_command', { command: action.command });
36
+ const stdout = result.stdout ?? '';
37
+ const stderr = result.stderr ?? '';
38
+ const output = [stdout, stderr].filter(Boolean).join('\n');
39
+ return output || 'Command executed (no output)';
40
+ }
41
+ default:
42
+ throw new Error(`Unknown action type: ${action.type}`);
43
+ }
44
+ }
45
+ //# sourceMappingURL=actions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"actions.js","sourceRoot":"","sources":["../src/actions.ts"],"names":[],"mappings":"AAeA,MAAM,CAAC,KAAK,UAAU,cAAc,CAClC,MAAsB,EACtB,MAAsB;IAEtB,QAAQ,MAAM,CAAC,IAAI,EAAE,CAAC;QACpB,KAAK,YAAY;YACf,OAAO,SAAS,CAAC;QAEnB,KAAK,OAAO,CAAC,CAAC,CAAC;YACb,MAAM,GAAG,GAAG,MAAM,CAAC,MAAM,KAAK,OAAO,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,YAAY,CAAC;YACrE,MAAM,MAAM,CAAC,WAAW,CAAC,GAAG,EAAE,EAAE,CAAC,EAAE,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC;YAC5D,OAAO,YAAY,MAAM,CAAC,MAAM,IAAI,MAAM,SAAS,MAAM,CAAC,CAAC,KAAK,MAAM,CAAC,CAAC,GAAG,CAAC;QAC9E,CAAC;QAED,KAAK,cAAc;YACjB,MAAM,MAAM,CAAC,WAAW,CAAC,cAAc,EAAE,EAAE,CAAC,EAAE,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC;YACvE,OAAO,sBAAsB,MAAM,CAAC,CAAC,KAAK,MAAM,CAAC,CAAC,GAAG,CAAC;QAExD,KAAK,MAAM;YACT,MAAM,MAAM,CAAC,WAAW,CAAC,WAAW,EAAE,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC;YAC7D,OAAO,UAAU,MAAM,CAAC,IAAI,GAAG,CAAC;QAElC,KAAK,UAAU;YACb,MAAM,MAAM,CAAC,WAAW,CAAC,QAAQ,EAAE,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC;YAC1D,OAAO,iBAAiB,MAAM,CAAC,IAAI,EAAE,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;QAEnD,KAAK,QAAQ;YACX,MAAM,MAAM,CAAC,WAAW,CAAC,QAAQ,EAAE;gBACjC,CAAC,EAAE,MAAM,CAAC,QAAQ,IAAI,CAAC;gBACvB,CAAC,EAAE,MAAM,CAAC,QAAQ,IAAI,CAAC;aACxB,CAAC,CAAC;YACH,OAAO,aAAa,MAAM,CAAC,QAAQ,IAAI,CAAC,KAAK,MAAM,CAAC,QAAQ,IAAI,CAAC,GAAG,CAAC;QAEvE,KAAK,MAAM;YACT,MAAM,MAAM,CAAC,WAAW,CAAC,aAAa,EAAE,EAAE,CAAC,EAAE,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC;YACtE,OAAO,oBAAoB,MAAM,CAAC,CAAC,KAAK,MAAM,CAAC,CAAC,GAAG,CAAC;QAEtD,KAAK,MAAM;YACT,MAAM,MAAM,CAAC,WAAW,CAAC,MAAM,EAAE,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,IAAI,MAAM,EAAE,CAAC,CAAC;YACzF,OAAO,iBAAiB,MAAM,CAAC,IAAI,EAAE,MAAM,IAAI,CAAC,SAAS,CAAC;QAE5D,KAAK,MAAM;YACT,MAAM,IAAI,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,UAAU,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,CAAC;YAC/C,OAAO,iBAAiB,CAAC;QAE3B,KAAK,aAAa,CAAC,CAAC,CAAC;YACnB,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,WAAW,CAAC,aAAa,EAAE,EAAE,OAAO,EAAE,MAAM,CAAC,OAAO,EAAE,CAAC,CAAC;YACpF,MAAM,MAAM,GAAI,MAAM,CAAC,MAAiB,IAAI,EAAE,CAAC;YAC/C,MAAM,MAAM,GAAI,MAAM,CAAC,MAAiB,IAAI,EAAE,CAAC;YAC/C,MAAM,MAAM,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAC3D,OAAO,MAAM,IAAI,8BAA8B,CAAC;QAClD,CAAC;QAED;YACE,MAAM,IAAI,KAAK,CAAC,wBAAwB,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC;IAC3D,CAAC;AACH,CAAC"}
@@ -0,0 +1,13 @@
1
+ import type { ComputerUseConfig } from './config.js';
2
+ export declare class ComputerClient {
3
+ private ws;
4
+ private commandLock;
5
+ private config;
6
+ constructor(config: ComputerUseConfig);
7
+ connect(): Promise<void>;
8
+ sendCommand(command: string, params?: Record<string, unknown>): Promise<Record<string, unknown>>;
9
+ screenshot(): Promise<string>;
10
+ close(): Promise<void>;
11
+ private authenticate;
12
+ }
13
+ //# sourceMappingURL=computer-client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"computer-client.d.ts","sourceRoot":"","sources":["../src/computer-client.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAIrD,qBAAa,cAAc;IACzB,OAAO,CAAC,EAAE,CAA0B;IACpC,OAAO,CAAC,WAAW,CAAuC;IAC1D,OAAO,CAAC,MAAM,CAAoB;gBAEtB,MAAM,EAAE,iBAAiB;IAI/B,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IA4BxB,WAAW,CACf,OAAO,EAAE,MAAM,EACf,MAAM,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,GACnC,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAqC7B,UAAU,IAAI,OAAO,CAAC,MAAM,CAAC;IAS7B,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAO5B,OAAO,CAAC,YAAY;CAyBrB"}
@@ -0,0 +1,109 @@
1
+ import WebSocket from 'ws';
2
+ const COMMAND_TIMEOUT_MS = 30_000;
3
+ export class ComputerClient {
4
+ ws = null;
5
+ commandLock = Promise.resolve();
6
+ config;
7
+ constructor(config) {
8
+ this.config = config;
9
+ }
10
+ async connect() {
11
+ const host = this.config.host ?? '127.0.0.1';
12
+ const port = this.config.port ?? 8000;
13
+ const protocol = this.config.apiKey ? 'wss' : 'ws';
14
+ const url = `${protocol}://${host}:${port}/ws`;
15
+ const headers = {};
16
+ if (this.config.apiKey)
17
+ headers['X-API-Key'] = this.config.apiKey;
18
+ if (this.config.vmName)
19
+ headers['X-VM-Name'] = this.config.vmName;
20
+ this.ws = await new Promise((resolve, reject) => {
21
+ const ws = new WebSocket(url, { headers });
22
+ ws.on('open', async () => {
23
+ if (this.config.apiKey && this.config.vmName) {
24
+ try {
25
+ await this.authenticate(ws);
26
+ }
27
+ catch (err) {
28
+ ws.close();
29
+ reject(err);
30
+ return;
31
+ }
32
+ }
33
+ resolve(ws);
34
+ });
35
+ ws.on('error', (err) => reject(new Error(`WebSocket connection failed: ${err.message}`)));
36
+ });
37
+ }
38
+ async sendCommand(command, params = {}) {
39
+ if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {
40
+ throw new Error('WebSocket not connected');
41
+ }
42
+ const result = await new Promise((resolve, reject) => {
43
+ const execute = () => new Promise((innerResolve, innerReject) => {
44
+ const timer = setTimeout(() => {
45
+ innerReject(new Error(`Command "${command}" timed out after ${COMMAND_TIMEOUT_MS}ms`));
46
+ }, COMMAND_TIMEOUT_MS);
47
+ const handler = (data) => {
48
+ clearTimeout(timer);
49
+ this.ws?.off('message', handler);
50
+ try {
51
+ const response = JSON.parse(data.toString());
52
+ if (response.error) {
53
+ innerReject(new Error(response.error));
54
+ }
55
+ else {
56
+ innerResolve(response);
57
+ }
58
+ }
59
+ catch (err) {
60
+ innerReject(err);
61
+ }
62
+ };
63
+ this.ws.on('message', handler);
64
+ this.ws.send(JSON.stringify({ command, params }));
65
+ });
66
+ this.commandLock = this.commandLock.then(() => execute().then(resolve, reject));
67
+ });
68
+ return result;
69
+ }
70
+ async screenshot() {
71
+ const response = await this.sendCommand('screenshot', {});
72
+ const imageData = response.image_data;
73
+ if (!imageData) {
74
+ throw new Error('No image_data in screenshot response');
75
+ }
76
+ return imageData;
77
+ }
78
+ async close() {
79
+ if (this.ws) {
80
+ this.ws.close();
81
+ this.ws = null;
82
+ }
83
+ }
84
+ authenticate(ws) {
85
+ return new Promise((resolve, reject) => {
86
+ const handler = (data) => {
87
+ ws.off('message', handler);
88
+ try {
89
+ const response = JSON.parse(data.toString());
90
+ if (response.success) {
91
+ resolve();
92
+ }
93
+ else {
94
+ reject(new Error(`Authentication failed: ${response.error ?? 'unknown error'}`));
95
+ }
96
+ }
97
+ catch (err) {
98
+ reject(err);
99
+ }
100
+ };
101
+ ws.on('message', handler);
102
+ ws.send(JSON.stringify({
103
+ command: 'authenticate',
104
+ params: { api_key: this.config.apiKey, container_name: this.config.vmName },
105
+ }));
106
+ });
107
+ }
108
+ }
109
+ //# sourceMappingURL=computer-client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"computer-client.js","sourceRoot":"","sources":["../src/computer-client.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,31 @@
1
+ export interface VisionModelConfig {
2
+ provider: string;
3
+ model: string;
4
+ apiKey?: string;
5
+ baseUrl?: string;
6
+ }
7
+ export interface ComputerUseConfig {
8
+ /** 'managed' spawns cua-computer-server, 'external' connects to existing */
9
+ mode?: 'managed' | 'external';
10
+ /** Command to start cua-computer-server (default: 'uvx') */
11
+ command?: string;
12
+ /** Package/module to run (default: 'cua-computer-server') */
13
+ package?: string;
14
+ /** Extra CLI args for cua-computer-server */
15
+ extraArgs?: string[];
16
+ /** Server host (default: '127.0.0.1') */
17
+ host?: string;
18
+ /** Server port (default: 8000) */
19
+ port?: number;
20
+ /** API key — enables wss + auth for cloud/remote */
21
+ apiKey?: string;
22
+ /** VM name for cloud routing */
23
+ vmName?: string;
24
+ /** Auto-screenshot after each action (default: true) */
25
+ autoScreenshot?: boolean;
26
+ /** Vision model for screenshot analysis */
27
+ visionModel?: VisionModelConfig;
28
+ }
29
+ export declare function resolveConfig(config?: ComputerUseConfig): ComputerUseConfig;
30
+ export declare function loadConfigFromFile(): ComputerUseConfig;
31
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,iBAAiB;IAChC,4EAA4E;IAC5E,IAAI,CAAC,EAAE,SAAS,GAAG,UAAU,CAAC;IAE9B,4DAA4D;IAC5D,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,6DAA6D;IAC7D,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,6CAA6C;IAC7C,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;IAErB,yCAAyC;IACzC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,kCAAkC;IAClC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,oDAAoD;IACpD,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,gCAAgC;IAChC,MAAM,CAAC,EAAE,MAAM,CAAC;IAEhB,wDAAwD;IACxD,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,2CAA2C;IAC3C,WAAW,CAAC,EAAE,iBAAiB,CAAC;CACjC;AAWD,wBAAgB,aAAa,CAAC,MAAM,CAAC,EAAE,iBAAiB,GAAG,iBAAiB,CAE3E;AAED,wBAAgB,kBAAkB,IAAI,iBAAiB,CAStD"}
package/dist/config.js ADDED
@@ -0,0 +1,25 @@
1
+ import { readFileSync } from 'node:fs';
2
+ import { resolve } from 'node:path';
3
+ const DEFAULTS = {
4
+ mode: 'managed',
5
+ command: 'uvx',
6
+ package: 'cua-computer-server',
7
+ host: '127.0.0.1',
8
+ port: 8000,
9
+ autoScreenshot: true,
10
+ };
11
+ export function resolveConfig(config) {
12
+ return { ...DEFAULTS, ...config };
13
+ }
14
+ export function loadConfigFromFile() {
15
+ try {
16
+ const configPath = resolve(process.cwd(), 'config.json');
17
+ const raw = readFileSync(configPath, 'utf-8');
18
+ const parsed = JSON.parse(raw);
19
+ return (parsed['pi-computer-use'] ?? {});
20
+ }
21
+ catch {
22
+ return {};
23
+ }
24
+ }
25
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAmCpC,MAAM,QAAQ,GAA+B;IAC3C,IAAI,EAAE,SAAS;IACf,OAAO,EAAE,KAAK;IACd,OAAO,EAAE,qBAAqB;IAC9B,IAAI,EAAE,WAAW;IACjB,IAAI,EAAE,IAAI;IACV,cAAc,EAAE,IAAI;CACrB,CAAC;AAEF,MAAM,UAAU,aAAa,CAAC,MAA0B;IACtD,OAAO,EAAE,GAAG,QAAQ,EAAE,GAAG,MAAM,EAAE,CAAC;AACpC,CAAC;AAED,MAAM,UAAU,kBAAkB;IAChC,IAAI,CAAC;QACH,MAAM,UAAU,GAAG,OAAO,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,aAAa,CAAC,CAAC;QACzD,MAAM,GAAG,GAAG,YAAY,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;QAC9C,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAA4B,CAAC;QAC1D,OAAO,CAAC,MAAM,CAAC,iBAAiB,CAAC,IAAI,EAAE,CAAsB,CAAC;IAChE,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAC;IACZ,CAAC;AACH,CAAC"}
@@ -0,0 +1,6 @@
1
+ import type { ExtensionAPI } from '@earendil-works/pi-coding-agent';
2
+ import { type ComputerUseConfig, loadConfigFromFile, resolveConfig } from './config.js';
3
+ export type { ComputerUseConfig };
4
+ export { loadConfigFromFile, resolveConfig };
5
+ export default function computerUseExtension(pi: ExtensionAPI): void;
6
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAoB,MAAM,iCAAiC,CAAC;AAItF,OAAO,EAAE,KAAK,iBAAiB,EAAE,kBAAkB,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAIxF,YAAY,EAAE,iBAAiB,EAAE,CAAC;AAClC,OAAO,EAAE,kBAAkB,EAAE,aAAa,EAAE,CAAC;AAE7C,MAAM,CAAC,OAAO,UAAU,oBAAoB,CAAC,EAAE,EAAE,YAAY,GAAG,IAAI,CA2HnE"}
package/dist/index.js ADDED
@@ -0,0 +1,102 @@
1
+ import { Type } from 'typebox';
2
+ import { dispatchAction } from './actions.js';
3
+ import { ComputerClient } from './computer-client.js';
4
+ import { loadConfigFromFile, resolveConfig } from './config.js';
5
+ import { ComputerServerProcess } from './server-process.js';
6
+ import { createPiVisionCaller } from './vision.js';
7
+ export { loadConfigFromFile, resolveConfig };
8
+ export default function computerUseExtension(pi) {
9
+ const config = resolveConfig(loadConfigFromFile());
10
+ const serverProcess = new ComputerServerProcess();
11
+ const client = new ComputerClient(config);
12
+ let started = false;
13
+ async function ensureRunning() {
14
+ if (started)
15
+ return;
16
+ if (config.mode !== 'external') {
17
+ await serverProcess.start(config);
18
+ }
19
+ await client.connect();
20
+ started = true;
21
+ }
22
+ pi.registerTool({
23
+ name: 'computer_use',
24
+ label: 'computer_use',
25
+ description: 'Control a computer desktop. Actions: screenshot, click, double_click, type, keypress, scroll, move, drag, wait, run_command. Each call executes one action and returns the resulting screen state.',
26
+ promptSnippet: 'computer_use — control a desktop: screenshot, click, type, scroll, keypress, drag, run_command',
27
+ parameters: Type.Object({
28
+ action: Type.Object({
29
+ type: Type.String({
30
+ description: 'Action type: screenshot | click | double_click | type | keypress | scroll | move | drag | wait | run_command',
31
+ }),
32
+ x: Type.Optional(Type.Number({ description: 'X coordinate (click, double_click, move)' })),
33
+ y: Type.Optional(Type.Number({ description: 'Y coordinate (click, double_click, move)' })),
34
+ button: Type.Optional(Type.String({ description: 'Mouse button: left | right (default: left)' })),
35
+ text: Type.Optional(Type.String({ description: 'Text to type (type action)' })),
36
+ keys: Type.Optional(Type.Array(Type.String(), { description: 'Keys to press (keypress action)' })),
37
+ scroll_x: Type.Optional(Type.Number({ description: 'Horizontal scroll amount' })),
38
+ scroll_y: Type.Optional(Type.Number({ description: 'Vertical scroll amount' })),
39
+ path: Type.Optional(Type.Array(Type.Tuple([Type.Number(), Type.Number()]), {
40
+ description: 'Drag path as [[x,y], ...] coordinates',
41
+ })),
42
+ command: Type.Optional(Type.String({ description: 'Shell command (run_command action)' })),
43
+ }),
44
+ }),
45
+ async execute(_toolCallId, params, _signal, _onUpdate, ctx) {
46
+ await ensureRunning();
47
+ if (params.action.type === 'screenshot') {
48
+ const screenshotBase64 = await client.screenshot();
49
+ if (config.visionModel) {
50
+ const callVision = createPiVisionCaller(config.visionModel, ctx);
51
+ const analysis = await callVision('Describe the full screen: identify all visible windows, UI elements, buttons, text fields, and their positions.', screenshotBase64, 'image/png');
52
+ return { content: [{ type: 'text', text: analysis }], details: undefined };
53
+ }
54
+ return {
55
+ content: [
56
+ {
57
+ type: 'text',
58
+ text: 'Screenshot captured (no vision model configured to analyze it).',
59
+ },
60
+ ],
61
+ details: undefined,
62
+ };
63
+ }
64
+ const actionResult = await dispatchAction(client, params.action);
65
+ if (config.autoScreenshot !== false) {
66
+ const screenshotBase64 = await client.screenshot();
67
+ if (config.visionModel) {
68
+ const callVision = createPiVisionCaller(config.visionModel, ctx);
69
+ const analysis = await callVision('Describe the current screen state after the action. Focus on what changed and what is now visible.', screenshotBase64, 'image/png');
70
+ return {
71
+ content: [
72
+ {
73
+ type: 'text',
74
+ text: `${actionResult}\n\nScreen state:\n${analysis}`,
75
+ },
76
+ ],
77
+ details: undefined,
78
+ };
79
+ }
80
+ return {
81
+ content: [{ type: 'text', text: actionResult ?? 'Action executed.' }],
82
+ details: undefined,
83
+ };
84
+ }
85
+ return {
86
+ content: [{ type: 'text', text: actionResult ?? 'Action executed.' }],
87
+ details: undefined,
88
+ };
89
+ },
90
+ });
91
+ pi.on('session_start', async () => {
92
+ // Lazy — actual startup happens on first tool call
93
+ });
94
+ pi.on('session_shutdown', async () => {
95
+ if (started) {
96
+ await client.close();
97
+ await serverProcess.stop();
98
+ started = false;
99
+ }
100
+ });
101
+ }
102
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ import type { ComputerUseConfig } from './config.js';
2
+ export declare class ComputerServerProcess {
3
+ private proc;
4
+ start(config: ComputerUseConfig): Promise<void>;
5
+ stop(): Promise<void>;
6
+ private waitForReady;
7
+ private probeWs;
8
+ }
9
+ //# sourceMappingURL=server-process.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server-process.d.ts","sourceRoot":"","sources":["../src/server-process.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAMrD,qBAAa,qBAAqB;IAChC,OAAO,CAAC,IAAI,CAA6B;IAEnC,KAAK,CAAC,MAAM,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC;IAqB/C,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;YAqBb,YAAY;IAoB1B,OAAO,CAAC,OAAO;CAqBhB"}
@@ -0,0 +1,76 @@
1
+ import { spawn } from 'node:child_process';
2
+ import WebSocket from 'ws';
3
+ const READY_TIMEOUT_MS = 30_000;
4
+ const POLL_INTERVAL_MS = 500;
5
+ const KILL_GRACE_MS = 2_000;
6
+ export class ComputerServerProcess {
7
+ proc = null;
8
+ async start(config) {
9
+ const cmd = config.command ?? 'uvx';
10
+ const pkg = config.package ?? 'cua-computer-server';
11
+ const host = config.host ?? '127.0.0.1';
12
+ const port = config.port ?? 8000;
13
+ const args = [pkg, '--host', host, '--port', String(port), ...(config.extraArgs ?? [])];
14
+ this.proc = spawn(cmd, args, { stdio: ['ignore', 'pipe', 'pipe'] });
15
+ this.proc.stderr?.on('data', (chunk) => {
16
+ const line = chunk.toString().trim();
17
+ if (line)
18
+ console.error(`[pi-computer-use] ${line}`);
19
+ });
20
+ this.proc.on('error', (err) => {
21
+ console.error(`[pi-computer-use] Failed to start ${cmd}: ${err.message}`);
22
+ });
23
+ await this.waitForReady(host, port);
24
+ }
25
+ async stop() {
26
+ if (!this.proc)
27
+ return;
28
+ const proc = this.proc;
29
+ this.proc = null;
30
+ proc.kill('SIGTERM');
31
+ await new Promise((resolve) => {
32
+ const timer = setTimeout(() => {
33
+ proc.kill('SIGKILL');
34
+ resolve();
35
+ }, KILL_GRACE_MS);
36
+ proc.on('exit', () => {
37
+ clearTimeout(timer);
38
+ resolve();
39
+ });
40
+ });
41
+ }
42
+ async waitForReady(host, port) {
43
+ const wsUrl = `ws://${host}:${port}/ws`;
44
+ const deadline = Date.now() + READY_TIMEOUT_MS;
45
+ while (Date.now() < deadline) {
46
+ if (this.proc?.exitCode !== null && this.proc?.exitCode !== undefined) {
47
+ throw new Error(`cua-computer-server exited with code ${this.proc.exitCode} before becoming ready`);
48
+ }
49
+ const ready = await this.probeWs(wsUrl);
50
+ if (ready)
51
+ return;
52
+ await new Promise((r) => setTimeout(r, POLL_INTERVAL_MS));
53
+ }
54
+ throw new Error(`cua-computer-server not ready after ${READY_TIMEOUT_MS}ms`);
55
+ }
56
+ probeWs(url) {
57
+ return new Promise((resolve) => {
58
+ const ws = new WebSocket(url);
59
+ const timer = setTimeout(() => {
60
+ ws.terminate();
61
+ resolve(false);
62
+ }, 1_000);
63
+ ws.on('open', () => {
64
+ clearTimeout(timer);
65
+ ws.close();
66
+ resolve(true);
67
+ });
68
+ ws.on('error', () => {
69
+ clearTimeout(timer);
70
+ ws.terminate();
71
+ resolve(false);
72
+ });
73
+ });
74
+ }
75
+ }
76
+ //# sourceMappingURL=server-process.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server-process.js","sourceRoot":"","sources":["../src/server-process.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ import type { ExtensionContext } from '@earendil-works/pi-coding-agent';
2
+ import type { VisionModelConfig } from './config.js';
3
+ export declare const VISUAL_SYSTEM_PROMPT = "You are a Visual Analysis Agent for computer-use automation.\nYou receive screenshots of a desktop and instructions about what to identify or analyze.\n\nYour responses must:\n- Describe UI elements with their exact pixel coordinates (x, y) relative to the top-left corner (0, 0)\n- Identify clickable elements (buttons, links, inputs, menus) and their bounding regions\n- Describe the current state of the screen (active window, focused element, visible text)\n- Note any dialogs, popups, or overlays that may block interaction\n- Provide spatial relationships between elements\n\nWhen reporting coordinates for click targets, give the CENTER point of the element.\nFormat coordinates as (x, y) inline with descriptions.\n\nIf an element is not visible on screen, explicitly state so.\nDo NOT perform actions \u2014 only analyze and report what you see.";
4
+ export type VisionCaller = (instruction: string, imageBase64: string, mimeType: string) => Promise<string>;
5
+ export declare function createPiVisionCaller(visionConfig: VisionModelConfig, ctx: ExtensionContext): VisionCaller;
6
+ //# sourceMappingURL=vision.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vision.d.ts","sourceRoot":"","sources":["../src/vision.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,iCAAiC,CAAC;AACxE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAErD,eAAO,MAAM,oBAAoB,21BAc8B,CAAC;AAEhE,MAAM,MAAM,YAAY,GAAG,CACzB,WAAW,EAAE,MAAM,EACnB,WAAW,EAAE,MAAM,EACnB,QAAQ,EAAE,MAAM,KACb,OAAO,CAAC,MAAM,CAAC,CAAC;AAErB,wBAAgB,oBAAoB,CAClC,YAAY,EAAE,iBAAiB,EAC/B,GAAG,EAAE,gBAAgB,GACpB,YAAY,CA+Cd"}
package/dist/vision.js ADDED
@@ -0,0 +1,57 @@
1
+ import { complete } from '@earendil-works/pi-ai';
2
+ export const VISUAL_SYSTEM_PROMPT = `You are a Visual Analysis Agent for computer-use automation.
3
+ You receive screenshots of a desktop and instructions about what to identify or analyze.
4
+
5
+ Your responses must:
6
+ - Describe UI elements with their exact pixel coordinates (x, y) relative to the top-left corner (0, 0)
7
+ - Identify clickable elements (buttons, links, inputs, menus) and their bounding regions
8
+ - Describe the current state of the screen (active window, focused element, visible text)
9
+ - Note any dialogs, popups, or overlays that may block interaction
10
+ - Provide spatial relationships between elements
11
+
12
+ When reporting coordinates for click targets, give the CENTER point of the element.
13
+ Format coordinates as (x, y) inline with descriptions.
14
+
15
+ If an element is not visible on screen, explicitly state so.
16
+ Do NOT perform actions — only analyze and report what you see.`;
17
+ export function createPiVisionCaller(visionConfig, ctx) {
18
+ return async (instruction, imageBase64, mimeType) => {
19
+ const model = ctx.modelRegistry.find(visionConfig.provider, visionConfig.model);
20
+ if (!model) {
21
+ throw new Error(`Vision model "${visionConfig.provider}/${visionConfig.model}" not found in model registry.`);
22
+ }
23
+ const auth = await ctx.modelRegistry.getApiKeyAndHeaders(model);
24
+ if (!auth.ok) {
25
+ throw new Error(`Auth failed for vision model: ${auth.error}`);
26
+ }
27
+ const options = {
28
+ temperature: 0,
29
+ maxTokens: 2048,
30
+ };
31
+ if (auth.apiKey)
32
+ options.apiKey = auth.apiKey;
33
+ if (auth.headers)
34
+ options.headers = auth.headers;
35
+ const result = await complete(model, {
36
+ systemPrompt: VISUAL_SYSTEM_PROMPT,
37
+ messages: [
38
+ {
39
+ role: 'user',
40
+ content: [
41
+ {
42
+ type: 'text',
43
+ text: `Analyze this screenshot and respond to the following instruction:\n\n${instruction}`,
44
+ },
45
+ { type: 'image', data: imageBase64, mimeType },
46
+ ],
47
+ timestamp: Date.now(),
48
+ },
49
+ ],
50
+ }, options);
51
+ return result.content
52
+ .filter((c) => c.type === 'text')
53
+ .map((c) => c.text)
54
+ .join('');
55
+ };
56
+ }
57
+ //# sourceMappingURL=vision.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vision.js","sourceRoot":"","sources":["../src/vision.ts"],"names":[],"mappings":"AAAA,OAAO,EAAqC,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAIpF,MAAM,CAAC,MAAM,oBAAoB,GAAG;;;;;;;;;;;;;;+DAc2B,CAAC;AAQhE,MAAM,UAAU,oBAAoB,CAClC,YAA+B,EAC/B,GAAqB;IAErB,OAAO,KAAK,EAAE,WAAmB,EAAE,WAAmB,EAAE,QAAgB,EAAmB,EAAE;QAC3F,MAAM,KAAK,GAAG,GAAG,CAAC,aAAa,CAAC,IAAI,CAAC,YAAY,CAAC,QAAQ,EAAE,YAAY,CAAC,KAAK,CAAC,CAAC;QAChF,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,MAAM,IAAI,KAAK,CACb,iBAAiB,YAAY,CAAC,QAAQ,IAAI,YAAY,CAAC,KAAK,gCAAgC,CAC7F,CAAC;QACJ,CAAC;QAED,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,aAAa,CAAC,mBAAmB,CAAC,KAAK,CAAC,CAAC;QAChE,IAAI,CAAC,IAAI,CAAC,EAAE,EAAE,CAAC;YACb,MAAM,IAAI,KAAK,CAAC,iCAAiC,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC;QACjE,CAAC;QAED,MAAM,OAAO,GAA4B;YACvC,WAAW,EAAE,CAAC;YACd,SAAS,EAAE,IAAI;SAChB,CAAC;QACF,IAAI,IAAI,CAAC,MAAM;YAAE,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC;QAC9C,IAAI,IAAI,CAAC,OAAO;YAAE,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC;QAEjD,MAAM,MAAM,GAAG,MAAM,QAAQ,CAC3B,KAAK,EACL;YACE,YAAY,EAAE,oBAAoB;YAClC,QAAQ,EAAE;gBACR;oBACE,IAAI,EAAE,MAAe;oBACrB,OAAO,EAAE;wBACP;4BACE,IAAI,EAAE,MAAe;4BACrB,IAAI,EAAE,wEAAwE,WAAW,EAAE;yBAC5F;wBACD,EAAE,IAAI,EAAE,OAAgB,EAAE,IAAI,EAAE,WAAW,EAAE,QAAQ,EAAE;qBACxD;oBACD,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE;iBACtB;aACF;SACF,EACD,OAAO,CACR,CAAC;QAEF,OAAO,MAAM,CAAC,OAAO;aAClB,MAAM,CAAC,CAAC,CAAC,EAAsB,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,MAAM,CAAC;aACpD,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;aAClB,IAAI,CAAC,EAAE,CAAC,CAAC;IACd,CAAC,CAAC;AACJ,CAAC"}
package/package.json ADDED
@@ -0,0 +1,52 @@
1
+ {
2
+ "name": "@amaster.ai/pi-computer-use",
3
+ "version": "0.1.1-beta.0",
4
+ "description": "Pi extension for CUA computer-use: spawns and controls cua-computer-server",
5
+ "license": "Apache-2.0",
6
+ "type": "module",
7
+ "sideEffects": false,
8
+ "main": "./dist/index.js",
9
+ "types": "./dist/index.d.ts",
10
+ "exports": {
11
+ ".": {
12
+ "types": "./dist/index.d.ts",
13
+ "default": "./dist/index.js"
14
+ },
15
+ "./package.json": {
16
+ "default": "./package.json"
17
+ }
18
+ },
19
+ "files": [
20
+ "dist",
21
+ "README.md"
22
+ ],
23
+ "publishConfig": {
24
+ "access": "public"
25
+ },
26
+ "repository": {
27
+ "type": "git",
28
+ "url": "https://github.com/TGYD-helige/pi.git",
29
+ "directory": "packages/pi-computer-use"
30
+ },
31
+ "dependencies": {
32
+ "ws": "^8.18.0"
33
+ },
34
+ "peerDependencies": {
35
+ "@earendil-works/pi-ai": ">=0.74.0",
36
+ "@earendil-works/pi-coding-agent": ">=0.74.0",
37
+ "typebox": "*"
38
+ },
39
+ "devDependencies": {
40
+ "@earendil-works/pi-ai": "0.74.0",
41
+ "@earendil-works/pi-coding-agent": "0.74.0",
42
+ "@types/ws": "^8.5.0",
43
+ "tsup": "^8.4.0",
44
+ "typebox": "*",
45
+ "vitest": "^4.0.0"
46
+ },
47
+ "scripts": {
48
+ "build": "tsup",
49
+ "typecheck": "tsc -b --pretty false",
50
+ "test": "vitest run src"
51
+ }
52
+ }