@elizaos/computeruse 0.24.20 → 0.24.21

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.
@@ -1,286 +0,0 @@
1
- const { Desktop } = require("../index.js");
2
-
3
- /**
4
- * Test for Locator.waitFor() method with 'exists' condition
5
- */
6
- async function testWaitForExists() {
7
- console.log("🕐 Testing Locator.waitFor('exists')...");
8
-
9
- try {
10
- const desktop = new Desktop();
11
-
12
- // Get any available application for testing
13
- const apps = desktop.applications();
14
- if (apps.length === 0) {
15
- throw new Error("No applications found for testing");
16
- }
17
-
18
- const testApp = apps[0];
19
- console.log(`📱 Testing with app: ${testApp.name()}`);
20
-
21
- // Test: Wait for a window to exist (should succeed immediately)
22
- console.log("Test: Wait for window to exist");
23
- const element = await desktop.locator("role:window").waitFor("exists", 5000);
24
-
25
- if (!element) {
26
- throw new Error("Expected element to be returned");
27
- }
28
-
29
- console.log(`✅ Found window: ${element.name()}`);
30
- return true;
31
- } catch (error) {
32
- console.error("❌ WaitFor exists test failed:", error.message);
33
- return false;
34
- }
35
- }
36
-
37
- /**
38
- * Test waitFor() with 'visible' condition
39
- */
40
- async function testWaitForVisible() {
41
- console.log("🕐 Testing Locator.waitFor('visible')...");
42
-
43
- try {
44
- const desktop = new Desktop();
45
-
46
- // Test: Wait for a visible window
47
- console.log("Test: Wait for window to be visible");
48
- const element = await desktop.locator("role:window").waitFor("visible", 5000);
49
-
50
- // Check that the element is actually visible
51
- if (!element.isVisible()) {
52
- throw new Error("Element should be visible");
53
- }
54
-
55
- console.log(`✅ Found visible window: ${element.name()}`);
56
- return true;
57
- } catch (error) {
58
- console.error("❌ WaitFor visible test failed:", error.message);
59
- return false;
60
- }
61
- }
62
-
63
- /**
64
- * Test waitFor() timeout behavior
65
- */
66
- async function testWaitForTimeout() {
67
- console.log("🕐 Testing Locator.waitFor() timeout...");
68
-
69
- try {
70
- const desktop = new Desktop();
71
-
72
- // Test: Wait for a non-existent element (should timeout)
73
- console.log("Test: Wait for non-existent element (expecting timeout)");
74
- let timedOut = false;
75
-
76
- try {
77
- await desktop
78
- .locator("role:button|ThisButtonDoesNotExist12345XYZ")
79
- .waitFor("exists", 1000);
80
- } catch (err) {
81
- if (err.message.includes("Timed out") || err.message.includes("timeout")) {
82
- timedOut = true;
83
- console.log("✅ Correctly timed out");
84
- } else {
85
- throw new Error(`Unexpected error: ${err.message}`);
86
- }
87
- }
88
-
89
- if (!timedOut) {
90
- throw new Error("Expected timeout error");
91
- }
92
-
93
- return true;
94
- } catch (error) {
95
- console.error("❌ WaitFor timeout test failed:", error.message);
96
- return false;
97
- }
98
- }
99
-
100
- /**
101
- * Test waitFor() with different conditions
102
- */
103
- async function testWaitForConditions() {
104
- console.log("🕐 Testing Locator.waitFor() with different conditions...");
105
-
106
- try {
107
- const desktop = new Desktop();
108
-
109
- // Test each condition on a window (which should be visible and enabled)
110
- const conditions = ["exists", "visible", "enabled"];
111
-
112
- for (const condition of conditions) {
113
- console.log(`Test: waitFor('${condition}')`);
114
- const element = await desktop
115
- .locator("role:window")
116
- .waitFor(condition, 5000);
117
-
118
- if (!element) {
119
- throw new Error(`No element returned for condition '${condition}'`);
120
- }
121
-
122
- console.log(`✅ Condition '${condition}' met`);
123
- }
124
-
125
- return true;
126
- } catch (error) {
127
- console.error("❌ WaitFor conditions test failed:", error.message);
128
- return false;
129
- }
130
- }
131
-
132
- /**
133
- * Test waitFor() with invalid condition
134
- */
135
- async function testWaitForInvalidCondition() {
136
- console.log("🕐 Testing Locator.waitFor() with invalid condition...");
137
-
138
- try {
139
- const desktop = new Desktop();
140
-
141
- // Test: Invalid condition should throw error
142
- console.log("Test: Wait with invalid condition");
143
- let errorThrown = false;
144
-
145
- try {
146
- await desktop.locator("role:window").waitFor("invalid_condition", 1000);
147
- } catch (err) {
148
- if (err.message.includes("Invalid condition")) {
149
- errorThrown = true;
150
- console.log("✅ Correctly rejected invalid condition");
151
- } else {
152
- throw new Error(`Unexpected error: ${err.message}`);
153
- }
154
- }
155
-
156
- if (!errorThrown) {
157
- throw new Error("Expected error for invalid condition");
158
- }
159
-
160
- return true;
161
- } catch (error) {
162
- console.error("❌ WaitFor invalid condition test failed:", error.message);
163
- return false;
164
- }
165
- }
166
-
167
- /**
168
- * Test waitFor() with chaining
169
- */
170
- async function testWaitForChaining() {
171
- console.log("🕐 Testing Locator.waitFor() with chaining...");
172
-
173
- try {
174
- const desktop = new Desktop();
175
- const apps = desktop.applications();
176
-
177
- if (apps.length === 0) {
178
- throw new Error("No applications found for testing");
179
- }
180
-
181
- const testApp = apps[0];
182
-
183
- // Test: Wait with chained locator
184
- console.log("Test: waitFor with chained locator");
185
- const element = await testApp
186
- .locator("role:window")
187
- .waitFor("visible", 3000);
188
-
189
- console.log(`✅ Found element via chain: ${element.name()}`);
190
- return true;
191
- } catch (error) {
192
- // This might fail if the app has no window, which is acceptable
193
- if (error.message.includes("Timed out")) {
194
- console.log("ℹ️ No window found in chain (acceptable)");
195
- return true;
196
- }
197
- console.error("❌ WaitFor chaining test failed:", error.message);
198
- return false;
199
- }
200
- }
201
-
202
- /**
203
- * Main test runner
204
- */
205
- async function runWaitForTests() {
206
- console.log("🚀 Starting Locator.waitFor() tests...\n");
207
-
208
- let passed = 0;
209
- let total = 0;
210
-
211
- // Test 1: Wait for exists
212
- total++;
213
- if (await testWaitForExists()) {
214
- passed++;
215
- }
216
-
217
- console.log(); // Empty line
218
-
219
- // Test 2: Wait for visible
220
- total++;
221
- if (await testWaitForVisible()) {
222
- passed++;
223
- }
224
-
225
- console.log(); // Empty line
226
-
227
- // Test 3: Timeout behavior
228
- total++;
229
- if (await testWaitForTimeout()) {
230
- passed++;
231
- }
232
-
233
- console.log(); // Empty line
234
-
235
- // Test 4: Different conditions
236
- total++;
237
- if (await testWaitForConditions()) {
238
- passed++;
239
- }
240
-
241
- console.log(); // Empty line
242
-
243
- // Test 5: Invalid condition
244
- total++;
245
- if (await testWaitForInvalidCondition()) {
246
- passed++;
247
- }
248
-
249
- console.log(); // Empty line
250
-
251
- // Test 6: Chaining
252
- total++;
253
- if (await testWaitForChaining()) {
254
- passed++;
255
- }
256
-
257
- console.log(); // Empty line
258
-
259
- // Results
260
- if (passed === total) {
261
- console.log(`🎉 All waitFor tests passed! (${passed}/${total})`);
262
- process.exit(0);
263
- } else {
264
- console.log(`❌ Some tests failed: ${passed}/${total} passed`);
265
- process.exit(1);
266
- }
267
- }
268
-
269
- // Export for use in other test files
270
- module.exports = {
271
- testWaitForExists,
272
- testWaitForVisible,
273
- testWaitForTimeout,
274
- testWaitForConditions,
275
- testWaitForInvalidCondition,
276
- testWaitForChaining,
277
- runWaitForTests,
278
- };
279
-
280
- // Run tests if this file is executed directly
281
- if (require.main === module) {
282
- runWaitForTests().catch((error) => {
283
- console.error("💥 Test runner crashed:", error);
284
- process.exit(1);
285
- });
286
- }
package/wrapper.ts DELETED
@@ -1,394 +0,0 @@
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";