@elizaos/computeruse 0.24.20
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/Cargo.toml +34 -0
- package/build.rs +10 -0
- package/computeruse.darwin-arm64.node +0 -0
- package/index.d.ts +0 -0
- package/index.js +327 -0
- package/package.json +74 -0
- package/scripts/sync-version.js +60 -0
- package/src/desktop.rs +2763 -0
- package/src/element.rs +1341 -0
- package/src/exceptions.rs +65 -0
- package/src/lib.rs +26 -0
- package/src/locator.rs +172 -0
- package/src/selector.rs +158 -0
- package/src/types.rs +963 -0
- package/src/window_manager.rs +342 -0
- package/tests/comprehensive-ui-elements.test.js +524 -0
- package/tests/cross-app-verification.test.js +243 -0
- package/tests/desktop-verify.test.js +169 -0
- package/tests/element-chaining.test.js +158 -0
- package/tests/element-range.test.js +207 -0
- package/tests/element-scroll-into-view.test.js +256 -0
- package/tests/element-value.test.js +264 -0
- package/tests/execute-browser-script-wrapper.test.js +135 -0
- package/tests/fixtures/sample-browser-script.js +7 -0
- package/tests/fixtures/script-with-env.js +16 -0
- package/tests/locator-validate.test.js +260 -0
- package/tests/locator-waitfor.test.js +286 -0
- package/wrapper.d.ts +84 -0
- package/wrapper.js +344 -0
- package/wrapper.ts +394 -0
package/wrapper.ts
ADDED
|
@@ -0,0 +1,394 @@
|
|
|
1
|
+
import * as native from "./index.js";
|
|
2
|
+
import * as util from "util";
|
|
3
|
+
import * as fs from "fs";
|
|
4
|
+
import * as path from "path";
|
|
5
|
+
|
|
6
|
+
// Type definitions for native classes
|
|
7
|
+
type NativeClass =
|
|
8
|
+
| typeof native.Desktop
|
|
9
|
+
| typeof native.Element
|
|
10
|
+
| typeof native.Locator
|
|
11
|
+
| typeof native.Selector;
|
|
12
|
+
|
|
13
|
+
function patchInspector(
|
|
14
|
+
Klass: any,
|
|
15
|
+
methodName = "toString",
|
|
16
|
+
forcePlainObject = false,
|
|
17
|
+
): void {
|
|
18
|
+
if (!Klass || typeof Klass !== "function") {
|
|
19
|
+
console.log("inspect not a function");
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
const proto = Klass.prototype;
|
|
23
|
+
const original = proto[util.inspect.custom];
|
|
24
|
+
proto[util.inspect.custom] = function (...args: any[]) {
|
|
25
|
+
if (typeof this[methodName] === "function") {
|
|
26
|
+
const result = this[methodName](...args);
|
|
27
|
+
if (forcePlainObject && result && typeof result === "object") {
|
|
28
|
+
return { ...result };
|
|
29
|
+
}
|
|
30
|
+
return result;
|
|
31
|
+
}
|
|
32
|
+
if (typeof original === "function") {
|
|
33
|
+
return original.apply(this, args);
|
|
34
|
+
}
|
|
35
|
+
return { ...this };
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function wrapNativeFunction<T extends Function>(fn: T): T {
|
|
40
|
+
if (typeof fn !== "function") return fn;
|
|
41
|
+
return function (this: any, ...args: any[]) {
|
|
42
|
+
try {
|
|
43
|
+
const result = fn.apply(this, args);
|
|
44
|
+
if (result instanceof Promise) {
|
|
45
|
+
return result.catch((error) => {
|
|
46
|
+
throw mapNativeError(error);
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
return result;
|
|
50
|
+
} catch (error) {
|
|
51
|
+
throw mapNativeError(error);
|
|
52
|
+
}
|
|
53
|
+
} as any;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function wrapClassMethods<T extends NativeClass>(Class: T): T {
|
|
57
|
+
const prototype = Class.prototype;
|
|
58
|
+
const methods = Object.getOwnPropertyNames(prototype);
|
|
59
|
+
methods.forEach((method) => {
|
|
60
|
+
if (method !== "constructor" && typeof prototype[method] === "function") {
|
|
61
|
+
prototype[method] = wrapNativeFunction(prototype[method]);
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
return Class;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function wrapClass<T extends NativeClass>(
|
|
68
|
+
Class: T,
|
|
69
|
+
...inspectOptions: any[]
|
|
70
|
+
): T {
|
|
71
|
+
const Wrapped = wrapClassMethods(Class);
|
|
72
|
+
patchInspector(Wrapped, ...(inspectOptions || []));
|
|
73
|
+
return Wrapped;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Custom error classes
|
|
77
|
+
export class ElementNotFoundError extends Error {
|
|
78
|
+
constructor(message: string) {
|
|
79
|
+
super(message);
|
|
80
|
+
this.name = "ElementNotFoundError";
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export class TimeoutError extends Error {
|
|
85
|
+
constructor(message: string) {
|
|
86
|
+
super(message);
|
|
87
|
+
this.name = "TimeoutError";
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export class PermissionDeniedError extends Error {
|
|
92
|
+
constructor(message: string) {
|
|
93
|
+
super(message);
|
|
94
|
+
this.name = "PermissionDeniedError";
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export class PlatformError extends Error {
|
|
99
|
+
constructor(message: string) {
|
|
100
|
+
super(message);
|
|
101
|
+
this.name = "PlatformError";
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export class UnsupportedOperationError extends Error {
|
|
106
|
+
constructor(message: string) {
|
|
107
|
+
super(message);
|
|
108
|
+
this.name = "UnsupportedOperationError";
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
export class UnsupportedPlatformError extends Error {
|
|
113
|
+
constructor(message: string) {
|
|
114
|
+
super(message);
|
|
115
|
+
this.name = "UnsupportedPlatformError";
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
export class InvalidArgumentError extends Error {
|
|
120
|
+
constructor(message: string) {
|
|
121
|
+
super(message);
|
|
122
|
+
this.name = "InvalidArgumentError";
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
export class InternalError extends Error {
|
|
127
|
+
constructor(message: string) {
|
|
128
|
+
super(message);
|
|
129
|
+
this.name = "InternalError";
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// Error mapping function
|
|
134
|
+
function mapNativeError(error: any): Error {
|
|
135
|
+
if (!error.message) return error;
|
|
136
|
+
|
|
137
|
+
const message = error.message;
|
|
138
|
+
if (message.startsWith("ELEMENT_NOT_FOUND:")) {
|
|
139
|
+
return new ElementNotFoundError(
|
|
140
|
+
message.replace("ELEMENT_NOT_FOUND:", "").trim(),
|
|
141
|
+
);
|
|
142
|
+
}
|
|
143
|
+
if (message.startsWith("OPERATION_TIMED_OUT:")) {
|
|
144
|
+
return new TimeoutError(message.replace("OPERATION_TIMED_OUT:", "").trim());
|
|
145
|
+
}
|
|
146
|
+
if (message.startsWith("PERMISSION_DENIED:")) {
|
|
147
|
+
return new PermissionDeniedError(
|
|
148
|
+
message.replace("PERMISSION_DENIED:", "").trim(),
|
|
149
|
+
);
|
|
150
|
+
}
|
|
151
|
+
if (message.startsWith("PLATFORM_ERROR:")) {
|
|
152
|
+
return new PlatformError(message.replace("PLATFORM_ERROR:", "").trim());
|
|
153
|
+
}
|
|
154
|
+
if (message.startsWith("UNSUPPORTED_OPERATION:")) {
|
|
155
|
+
return new UnsupportedOperationError(
|
|
156
|
+
message.replace("UNSUPPORTED_OPERATION:", "").trim(),
|
|
157
|
+
);
|
|
158
|
+
}
|
|
159
|
+
if (message.startsWith("UNSUPPORTED_PLATFORM:")) {
|
|
160
|
+
return new UnsupportedPlatformError(
|
|
161
|
+
message.replace("UNSUPPORTED_PLATFORM:", "").trim(),
|
|
162
|
+
);
|
|
163
|
+
}
|
|
164
|
+
if (message.startsWith("INVALID_ARGUMENT:")) {
|
|
165
|
+
return new InvalidArgumentError(
|
|
166
|
+
message.replace("INVALID_ARGUMENT:", "").trim(),
|
|
167
|
+
);
|
|
168
|
+
}
|
|
169
|
+
if (message.startsWith("INTERNAL_ERROR:")) {
|
|
170
|
+
return new InternalError(message.replace("INTERNAL_ERROR:", "").trim());
|
|
171
|
+
}
|
|
172
|
+
return error;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// Types for executeBrowserScript arguments
|
|
176
|
+
export type BrowserScriptEnv = Record<string, unknown>;
|
|
177
|
+
export type BrowserScriptFunction = (env?: BrowserScriptEnv) => any;
|
|
178
|
+
export type BrowserScriptOptions = {
|
|
179
|
+
file: string;
|
|
180
|
+
env?: BrowserScriptEnv;
|
|
181
|
+
};
|
|
182
|
+
type BrowserScriptInput = string | BrowserScriptFunction | BrowserScriptOptions;
|
|
183
|
+
|
|
184
|
+
// Enhanced executeBrowserScript with function and file support
|
|
185
|
+
// Desktop signature: (script, process, timeoutMs?)
|
|
186
|
+
// Element signature: (script)
|
|
187
|
+
async function enhancedExecuteBrowserScript(
|
|
188
|
+
this: any,
|
|
189
|
+
scriptOrFunction: BrowserScriptInput,
|
|
190
|
+
processOrEnv?: string | any,
|
|
191
|
+
timeoutMs?: number,
|
|
192
|
+
): Promise<any> {
|
|
193
|
+
// Detect if this is Desktop or Element
|
|
194
|
+
// Can't use .length on napi functions (always returns 0), so check constructor name
|
|
195
|
+
const isDesktop = this.constructor?.name === 'Desktop';
|
|
196
|
+
|
|
197
|
+
// For Desktop: second param is process name, third is timeout
|
|
198
|
+
// For Element: second param is env options (backward compatible)
|
|
199
|
+
let process: string | undefined;
|
|
200
|
+
let envOrOptions: any;
|
|
201
|
+
|
|
202
|
+
if (isDesktop) {
|
|
203
|
+
if (typeof processOrEnv !== 'string') {
|
|
204
|
+
throw new Error(
|
|
205
|
+
"Desktop.executeBrowserScript requires 'process' as second argument (e.g., 'chrome', 'msedge', 'firefox'). " +
|
|
206
|
+
"Usage: desktop.executeBrowserScript(script, 'chrome', timeoutMs?)"
|
|
207
|
+
);
|
|
208
|
+
}
|
|
209
|
+
process = processOrEnv;
|
|
210
|
+
} else {
|
|
211
|
+
// Element - second param is env options
|
|
212
|
+
envOrOptions = processOrEnv;
|
|
213
|
+
}
|
|
214
|
+
let script: string;
|
|
215
|
+
let env: any = {};
|
|
216
|
+
let shouldInjectEnv = false; // Only inject env for file-based and string scripts, not functions
|
|
217
|
+
|
|
218
|
+
// Handle different input types
|
|
219
|
+
if (typeof scriptOrFunction === "string") {
|
|
220
|
+
// Check if it's a file path
|
|
221
|
+
if (scriptOrFunction.endsWith(".ts") || scriptOrFunction.endsWith(".js")) {
|
|
222
|
+
// File path - read and compile
|
|
223
|
+
const filePath = path.resolve(scriptOrFunction);
|
|
224
|
+
if (!fs.existsSync(filePath)) {
|
|
225
|
+
throw new Error(`Browser script file not found: ${filePath}`);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
let fileContent = fs.readFileSync(filePath, "utf-8");
|
|
229
|
+
|
|
230
|
+
// If TypeScript, compile it
|
|
231
|
+
if (filePath.endsWith(".ts")) {
|
|
232
|
+
try {
|
|
233
|
+
const esbuild = require("esbuild");
|
|
234
|
+
const result = await esbuild.transform(fileContent, {
|
|
235
|
+
loader: "ts",
|
|
236
|
+
target: "es2020",
|
|
237
|
+
format: "iife",
|
|
238
|
+
});
|
|
239
|
+
fileContent = result.code;
|
|
240
|
+
} catch (e: any) {
|
|
241
|
+
// If esbuild not available, try to use as-is (may work for simple TS)
|
|
242
|
+
console.warn(
|
|
243
|
+
"esbuild not found - using TypeScript file as-is:",
|
|
244
|
+
e.message,
|
|
245
|
+
);
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
script = fileContent;
|
|
250
|
+
env = envOrOptions || {};
|
|
251
|
+
shouldInjectEnv = true; // Inject env for file paths passed as strings
|
|
252
|
+
} else {
|
|
253
|
+
// Plain string script - use as-is (backward compatible)
|
|
254
|
+
script = scriptOrFunction;
|
|
255
|
+
}
|
|
256
|
+
} else if (typeof scriptOrFunction === "function") {
|
|
257
|
+
// Function - convert to IIFE with proper wrapping
|
|
258
|
+
const funcString = scriptOrFunction.toString();
|
|
259
|
+
env = envOrOptions || {};
|
|
260
|
+
|
|
261
|
+
// Wrap function in IIFE that handles return values
|
|
262
|
+
script = `
|
|
263
|
+
(async function() {
|
|
264
|
+
const fn = ${funcString};
|
|
265
|
+
const result = await fn(${JSON.stringify(env)});
|
|
266
|
+
|
|
267
|
+
// Auto-stringify result if it's an object
|
|
268
|
+
if (result !== undefined && result !== null) {
|
|
269
|
+
if (typeof result === 'object') {
|
|
270
|
+
return JSON.stringify(result);
|
|
271
|
+
}
|
|
272
|
+
return String(result);
|
|
273
|
+
}
|
|
274
|
+
return "undefined";
|
|
275
|
+
})()
|
|
276
|
+
`;
|
|
277
|
+
} else if (
|
|
278
|
+
typeof scriptOrFunction === "object" &&
|
|
279
|
+
(scriptOrFunction as BrowserScriptOptions).file
|
|
280
|
+
) {
|
|
281
|
+
// Object with file property
|
|
282
|
+
const options = scriptOrFunction as BrowserScriptOptions;
|
|
283
|
+
const filePath = path.resolve(options.file);
|
|
284
|
+
if (!fs.existsSync(filePath)) {
|
|
285
|
+
throw new Error(`Browser script file not found: ${filePath}`);
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
let fileContent = fs.readFileSync(filePath, "utf-8");
|
|
289
|
+
|
|
290
|
+
// If TypeScript, compile it
|
|
291
|
+
if (filePath.endsWith(".ts")) {
|
|
292
|
+
try {
|
|
293
|
+
const esbuild = require("esbuild");
|
|
294
|
+
const result = await esbuild.transform(fileContent, {
|
|
295
|
+
loader: "ts",
|
|
296
|
+
target: "es2020",
|
|
297
|
+
format: "iife",
|
|
298
|
+
});
|
|
299
|
+
fileContent = result.code;
|
|
300
|
+
} catch (e: any) {
|
|
301
|
+
console.warn(
|
|
302
|
+
"esbuild not found - using TypeScript file as-is:",
|
|
303
|
+
e.message,
|
|
304
|
+
);
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
script = fileContent;
|
|
309
|
+
env = options.env || {};
|
|
310
|
+
shouldInjectEnv = true; // Inject env for file option objects
|
|
311
|
+
} else {
|
|
312
|
+
throw new Error(
|
|
313
|
+
"Invalid argument to executeBrowserScript: expected string, function, or {file, env} object",
|
|
314
|
+
);
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
// If env variables are provided and we should inject them (file-based scripts only)
|
|
318
|
+
if (shouldInjectEnv && env && Object.keys(env).length > 0) {
|
|
319
|
+
// Inject as an env object that scripts can access
|
|
320
|
+
const envObject = `const env = ${JSON.stringify(env)};`;
|
|
321
|
+
script = `${envObject}\n${script}`;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
// Call the original native method
|
|
325
|
+
// Desktop requires (script, process, timeoutMs?), Element only needs (script)
|
|
326
|
+
const resultStr = isDesktop
|
|
327
|
+
? await this._originalExecuteBrowserScript(script, process, timeoutMs)
|
|
328
|
+
: await this._originalExecuteBrowserScript(script);
|
|
329
|
+
|
|
330
|
+
// If function was passed, try to parse JSON result
|
|
331
|
+
if (typeof scriptOrFunction === "function") {
|
|
332
|
+
try {
|
|
333
|
+
return JSON.parse(resultStr);
|
|
334
|
+
} catch (e) {
|
|
335
|
+
// Not JSON, return as-is
|
|
336
|
+
return resultStr;
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
// For string/file, return raw result (backward compatible)
|
|
341
|
+
return resultStr;
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
// Wrap the native classes
|
|
345
|
+
export const Desktop = wrapClassMethods(native.Desktop);
|
|
346
|
+
export const Element = wrapClass(native.Element);
|
|
347
|
+
export const Locator = wrapClass(native.Locator);
|
|
348
|
+
export const Selector = wrapClass(native.Selector);
|
|
349
|
+
export const WindowManager = wrapClassMethods(native.WindowManager);
|
|
350
|
+
|
|
351
|
+
// Patch executeBrowserScript on Desktop and Element
|
|
352
|
+
if (Desktop.prototype.executeBrowserScript) {
|
|
353
|
+
(Desktop.prototype as any)._originalExecuteBrowserScript =
|
|
354
|
+
Desktop.prototype.executeBrowserScript;
|
|
355
|
+
Desktop.prototype.executeBrowserScript = enhancedExecuteBrowserScript;
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
if (Element.prototype.executeBrowserScript) {
|
|
359
|
+
(Element.prototype as any)._originalExecuteBrowserScript =
|
|
360
|
+
Element.prototype.executeBrowserScript;
|
|
361
|
+
Element.prototype.executeBrowserScript = enhancedExecuteBrowserScript;
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
// Re-export native types
|
|
365
|
+
export type {
|
|
366
|
+
ValidationResult,
|
|
367
|
+
Bounds,
|
|
368
|
+
Coordinates,
|
|
369
|
+
ClickResult,
|
|
370
|
+
CommandOutput,
|
|
371
|
+
Monitor,
|
|
372
|
+
MonitorScreenshotPair,
|
|
373
|
+
ScreenshotResult,
|
|
374
|
+
UIElementAttributes,
|
|
375
|
+
UINode,
|
|
376
|
+
WindowInfo,
|
|
377
|
+
ActionOptions,
|
|
378
|
+
TypeTextOptions,
|
|
379
|
+
PressKeyOptions,
|
|
380
|
+
// Computer Use types
|
|
381
|
+
ComputerUseResult,
|
|
382
|
+
ComputerUseStep,
|
|
383
|
+
ComputerUsePendingConfirmation,
|
|
384
|
+
} from "./index.js";
|
|
385
|
+
export {
|
|
386
|
+
ClickType,
|
|
387
|
+
ClusteredBoundsEntry,
|
|
388
|
+
ClusteredFormattingResult,
|
|
389
|
+
ElementSource,
|
|
390
|
+
PropertyLoadingMode,
|
|
391
|
+
TextPosition,
|
|
392
|
+
TreeOutputFormat,
|
|
393
|
+
VisionType,
|
|
394
|
+
} from "./index.js";
|