@aspiresys/visor 1.1.8 → 1.1.10

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/dist/app.d.ts CHANGED
@@ -43,3 +43,5 @@ export declare function openApp(target: string): Promise<void>;
43
43
  * await closeApp("ms-teams.exe");
44
44
  */
45
45
  export declare function closeApp(processName: string): Promise<void>;
46
+ export declare function execCommand(command: string): Promise<string>;
47
+ export declare function execDetached(command: string): Promise<void>;
package/dist/app.js CHANGED
@@ -2,6 +2,8 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.openApp = openApp;
4
4
  exports.closeApp = closeApp;
5
+ exports.execCommand = execCommand;
6
+ exports.execDetached = execDetached;
5
7
  const child_process_1 = require("child_process");
6
8
  const logger_1 = require("./logger");
7
9
  function execAsync(command) {
@@ -98,3 +100,23 @@ async function closeApp(processName) {
98
100
  });
99
101
  });
100
102
  }
103
+ async function execCommand(command) {
104
+ (0, logger_1.log)(`[CMD] Executing: ${command}`);
105
+ return new Promise((resolve, reject) => {
106
+ (0, child_process_1.exec)(command, (error, stdout, stderr) => {
107
+ if (error) {
108
+ reject(new Error(stderr || error.message));
109
+ return;
110
+ }
111
+ resolve(stdout.trim());
112
+ });
113
+ });
114
+ }
115
+ async function execDetached(command) {
116
+ (0, logger_1.log)(`[CMD] Starting detached: ${command}`);
117
+ (0, child_process_1.spawn)(command, [], {
118
+ detached: true,
119
+ shell: true,
120
+ stdio: "ignore",
121
+ }).unref();
122
+ }
package/dist/config.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  export declare const visorConfig: {
2
+ initialized: boolean;
2
3
  scaleFactor: number;
3
4
  debug: boolean;
4
5
  imagePath: string;
package/dist/config.js CHANGED
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.visorConfig = void 0;
4
4
  exports.visorConfig = {
5
+ initialized: false,
5
6
  scaleFactor: 1,
6
7
  debug: true,
7
8
  imagePath: "",
package/dist/index.d.ts CHANGED
@@ -4,6 +4,8 @@ export declare class Visor {
4
4
  keyboard: import("@nut-tree-fork/nut-js").KeyboardClass;
5
5
  Key: typeof Key;
6
6
  Button: typeof Button;
7
+ private configWarningShown;
8
+ private checkConfig;
7
9
  /**
8
10
  * Finds an image on screen using OpenCV
9
11
  * and performs a left mouse click
@@ -903,5 +905,23 @@ export declare class Visor {
903
905
  * await visor.terminateOCR();
904
906
  */
905
907
  terminateOCR(): Promise<void>;
908
+ /**
909
+ * Executes a command and returns output.
910
+ *
911
+ * @example
912
+ * const output =
913
+ * await visor.exec("ipconfig");
914
+ */
915
+ exec(command: string): Promise<string>;
916
+ /**
917
+ * Starts a command without waiting
918
+ * for completion.
919
+ *
920
+ * @example
921
+ * await visor.execDetached(
922
+ * "notepad"
923
+ * );
924
+ */
925
+ execDetached(command: string): Promise<void>;
906
926
  }
907
927
  export declare const visor: Visor;
package/dist/index.js CHANGED
@@ -12,12 +12,40 @@ const nut_js_1 = require("@nut-tree-fork/nut-js");
12
12
  const path_1 = require("./path");
13
13
  const display_1 = require("./display");
14
14
  const logger_1 = require("./logger");
15
+ const console_1 = require("console");
15
16
  class Visor {
16
17
  constructor() {
17
18
  this.mouse = nut_js_1.mouse;
18
19
  this.keyboard = nut_js_1.keyboard;
19
20
  this.Key = nut_js_1.Key;
20
21
  this.Button = nut_js_1.Button;
22
+ this.configWarningShown = false;
23
+ }
24
+ checkConfig() {
25
+ if (this.configWarningShown) {
26
+ return;
27
+ }
28
+ const missing = [];
29
+ if (!config_1.visorConfig.imagePath) {
30
+ missing.push("imagePath");
31
+ }
32
+ if (!config_1.visorConfig.outputPath) {
33
+ missing.push("outputPath");
34
+ }
35
+ if (missing.length > 0) {
36
+ (0, console_1.warn)(`[VISOR] Configuration incomplete.\n` +
37
+ `Missing: ${missing.join(", ")}\n\n` +
38
+ `Relative paths may not work correctly.\n` +
39
+ `Use visor.loadConfig(...) or provide absolute paths directly to functions.`);
40
+ }
41
+ if (!config_1.visorConfig.initialized) {
42
+ config_1.visorConfig.scaleFactor = (0, display_1.getWindowsScaleFactor)();
43
+ (0, console_1.warn)(`[VISOR] Configuration not loaded.\n` +
44
+ `Auto-detected scaleFactor=${config_1.visorConfig.scaleFactor}.\n`);
45
+ }
46
+ if (missing.length > 0 || !config_1.visorConfig.initialized) {
47
+ this.configWarningShown = true;
48
+ }
21
49
  }
22
50
  /**
23
51
  * Finds an image on screen using OpenCV
@@ -50,6 +78,7 @@ class Visor {
50
78
  * await visor.click("./images/save.png", 0.9);
51
79
  */
52
80
  async click(image, confidence = 0.8) {
81
+ this.checkConfig();
53
82
  if (!image) {
54
83
  await this.mouse.click(this.Button.LEFT);
55
84
  (0, logger_1.log)("[MOUSE] Left click completed");
@@ -87,6 +116,7 @@ class Visor {
87
116
  * await visor.rightClick();
88
117
  */
89
118
  async rightClick(image, confidence = 0.8) {
119
+ this.checkConfig();
90
120
  if (image) {
91
121
  const region = await this.find(image, confidence);
92
122
  if (!region) {
@@ -123,6 +153,7 @@ class Visor {
123
153
  * await visor.doubleClick();
124
154
  */
125
155
  async doubleClick(image, confidence = 0.8) {
156
+ this.checkConfig();
126
157
  if (image) {
127
158
  const region = await this.find(image, confidence);
128
159
  if (!region) {
@@ -163,10 +194,14 @@ class Visor {
163
194
  * await visor.find("save.png");
164
195
  */
165
196
  async find(image, confidence = 0.8) {
197
+ this.checkConfig();
198
+ const start = Date.now();
166
199
  const screen = await (0, screen_1.captureScreen)();
167
200
  const template = await (0, matcher_1.loadTemplate)((0, path_1.resolveImagePath)(image));
168
201
  try {
169
- return (0, matcher_1.matchTemplate)(screen, template, confidence);
202
+ const result = (0, matcher_1.matchTemplate)(screen, template, confidence);
203
+ (0, logger_1.log)(`[FIND] Total: ${Date.now() - start} ms`);
204
+ return result;
170
205
  }
171
206
  finally {
172
207
  screen.delete();
@@ -193,6 +228,7 @@ class Visor {
193
228
  * await visor.exists("login.png");
194
229
  */
195
230
  async exists(image, confidence = 0.8) {
231
+ this.checkConfig();
196
232
  return (await this.find(image, confidence)) !== null;
197
233
  }
198
234
  /**
@@ -218,6 +254,7 @@ class Visor {
218
254
  * await visor.findAll("icon.png");
219
255
  */
220
256
  async findAll(image, confidence = 0.8) {
257
+ this.checkConfig();
221
258
  const screen = await (0, screen_1.captureScreen)();
222
259
  const template = await (0, matcher_1.loadTemplate)((0, path_1.resolveImagePath)(image));
223
260
  try {
@@ -251,6 +288,7 @@ class Visor {
251
288
  * await visor.previewMatches("button.png");
252
289
  */
253
290
  async previewMatches(image, confidence = 0.8) {
291
+ this.checkConfig();
254
292
  const matches = await this.findAll(image, confidence);
255
293
  (0, logger_1.log)("\n=== MATCH PREVIEW ===\n");
256
294
  matches.forEach((m, i) => {
@@ -298,6 +336,7 @@ CONF:${m.confidence.toFixed(3)}
298
336
  * await visor.wait("loading-done.png");
299
337
  */
300
338
  async wait(image, { confidence = 0.8, timeout = 5000 } = {}) {
339
+ this.checkConfig();
301
340
  const interval = 300;
302
341
  const attempts = Math.ceil(timeout / interval);
303
342
  for (let i = 0; i < attempts; i++) {
@@ -334,6 +373,7 @@ CONF:${m.confidence.toFixed(3)}
334
373
  * await visor.waitToVanish("spinner.png");
335
374
  */
336
375
  async waitToVanish(image, { confidence = 0.8, timeout = 5000 } = {}) {
376
+ this.checkConfig();
337
377
  const start = Date.now();
338
378
  while (Date.now() - start < timeout) {
339
379
  if (!(await this.exists(image, confidence))) {
@@ -369,6 +409,7 @@ CONF:${m.confidence.toFixed(3)}
369
409
  * await visor.waitText("Success");
370
410
  */
371
411
  async waitText(text, timeout = 5000) {
412
+ this.checkConfig();
372
413
  const start = Date.now();
373
414
  while (Date.now() - start < timeout) {
374
415
  if (await this.existsText(text)) {
@@ -402,6 +443,7 @@ CONF:${m.confidence.toFixed(3)}
402
443
  * await visor.waitTextToVanish("Loading");
403
444
  */
404
445
  async waitTextToVanish(text, timeout = 5000) {
446
+ this.checkConfig();
405
447
  const start = Date.now();
406
448
  while (Date.now() - start < timeout) {
407
449
  if (!(await this.existsText(text))) {
@@ -425,6 +467,7 @@ CONF:${m.confidence.toFixed(3)}
425
467
  * await visor.clickText("Login");
426
468
  */
427
469
  async clickText(text, index = 0) {
470
+ this.checkConfig();
428
471
  const region = await (0, text_1.findText)(text, index);
429
472
  if (!region) {
430
473
  throw new Error(`Text not found: ${text}`);
@@ -446,6 +489,7 @@ CONF:${m.confidence.toFixed(3)}
446
489
  * await visor.findText("Submit");
447
490
  */
448
491
  async findText(text, index = 0, region) {
492
+ this.checkConfig();
449
493
  return await (0, text_1.findText)(text, index, region);
450
494
  }
451
495
  /**
@@ -462,6 +506,7 @@ CONF:${m.confidence.toFixed(3)}
462
506
  * await visor.existsText("Teams");
463
507
  */
464
508
  async existsText(text, region) {
509
+ this.checkConfig();
465
510
  return await (0, text_1.existsText)(text, region);
466
511
  }
467
512
  /**
@@ -482,6 +527,7 @@ CONF:${m.confidence.toFixed(3)}
482
527
  * });
483
528
  */
484
529
  async readRegion(region) {
530
+ this.checkConfig();
485
531
  return await (0, ocr_1.extractTextFromRegion)(region);
486
532
  }
487
533
  /**
@@ -516,6 +562,7 @@ CONF:${m.confidence.toFixed(3)}
516
562
  * log(result.text);
517
563
  */
518
564
  async readScreen() {
565
+ this.checkConfig();
519
566
  return await (0, ocr_1.extractTextFromRegion)();
520
567
  }
521
568
  /**
@@ -534,6 +581,7 @@ CONF:${m.confidence.toFixed(3)}
534
581
  * );
535
582
  */
536
583
  async captureScreenshot(path) {
584
+ this.checkConfig();
537
585
  path = config_1.visorConfig.outputPath + "/" + path;
538
586
  (0, logger_1.log)(`[VISOR] Saving screenshot: ${path}`);
539
587
  await (0, screen_1.saveScreenshot)(path);
@@ -594,6 +642,7 @@ CONF:${m.confidence.toFixed(3)}
594
642
  * );
595
643
  */
596
644
  async dragDrop(source, target, confidence = 0.8) {
645
+ this.checkConfig();
597
646
  const src = await this.find(source, confidence);
598
647
  const dst = await this.find(target, confidence);
599
648
  if (!src || !dst) {
@@ -625,6 +674,7 @@ CONF:${m.confidence.toFixed(3)}
625
674
  * await visor.hover("menu.png");
626
675
  */
627
676
  async hover(image, confidence = 0.8) {
677
+ this.checkConfig();
628
678
  const region = await this.find(image, confidence);
629
679
  if (!region) {
630
680
  throw new Error(`Image not found: ${image}`);
@@ -647,6 +697,7 @@ CONF:${m.confidence.toFixed(3)}
647
697
  * await visor.scrollDown(1000);
648
698
  */
649
699
  async scrollDown(amount = 500) {
700
+ this.checkConfig();
650
701
  await this.mouse.scrollDown(amount);
651
702
  }
652
703
  /**
@@ -664,6 +715,7 @@ CONF:${m.confidence.toFixed(3)}
664
715
  * await visor.scrollUp(1000);
665
716
  */
666
717
  async scrollUp(amount = 500) {
718
+ this.checkConfig();
667
719
  await this.mouse.scrollUp(amount);
668
720
  }
669
721
  /**
@@ -741,6 +793,7 @@ CONF:${m.confidence.toFixed(3)}
741
793
  * await visor.moveMouse(500, 300);
742
794
  */
743
795
  async moveMouse(x, y) {
796
+ this.checkConfig();
744
797
  await this.mouse.move((0, nut_js_1.straightTo)(new nut_js_1.Point(x, y)));
745
798
  }
746
799
  /**
@@ -754,6 +807,7 @@ CONF:${m.confidence.toFixed(3)}
754
807
  * await visor.getMousePosition();
755
808
  */
756
809
  async getMousePosition() {
810
+ this.checkConfig();
757
811
  return await this.mouse.getPosition();
758
812
  }
759
813
  /**
@@ -920,6 +974,7 @@ CONF:${m.confidence.toFixed(3)}
920
974
  if (config.debug !== undefined) {
921
975
  this.setDebug(config.debug);
922
976
  }
977
+ config_1.visorConfig.initialized = true;
923
978
  (0, logger_1.log)("[VISOR] Config loaded");
924
979
  }
925
980
  /**
@@ -951,6 +1006,7 @@ CONF:${m.confidence.toFixed(3)}
951
1006
  * ]);
952
1007
  */
953
1008
  async findAny(images, confidence = 0.8) {
1009
+ this.checkConfig();
954
1010
  const screen = await (0, screen_1.captureScreen)();
955
1011
  try {
956
1012
  for (const image of images) {
@@ -1002,6 +1058,7 @@ CONF:${m.confidence.toFixed(3)}
1002
1058
  * ]);
1003
1059
  */
1004
1060
  async existsAny(images, confidence = 0.8) {
1061
+ this.checkConfig();
1005
1062
  return (await this.findAny(images, confidence)) !== null;
1006
1063
  }
1007
1064
  /**
@@ -1044,6 +1101,7 @@ CONF:${m.confidence.toFixed(3)}
1044
1101
  * ]);
1045
1102
  */
1046
1103
  async clickAny(images, confidence = 0.8) {
1104
+ this.checkConfig();
1047
1105
  const result = await this.findAny(images, confidence);
1048
1106
  if (!result) {
1049
1107
  throw new Error(`None of the images were found`);
@@ -1098,6 +1156,7 @@ CONF:${m.confidence.toFixed(3)}
1098
1156
  * ]);
1099
1157
  */
1100
1158
  async waitAny(images, { confidence = 0.8, timeout = 5000 } = {}) {
1159
+ this.checkConfig();
1101
1160
  const interval = 300;
1102
1161
  const attempts = Math.ceil(timeout / interval);
1103
1162
  for (let i = 0; i < attempts; i++) {
@@ -1130,6 +1189,28 @@ CONF:${m.confidence.toFixed(3)}
1130
1189
  async terminateOCR() {
1131
1190
  await (0, ocr_1.terminateOCR)();
1132
1191
  }
1192
+ /**
1193
+ * Executes a command and returns output.
1194
+ *
1195
+ * @example
1196
+ * const output =
1197
+ * await visor.exec("ipconfig");
1198
+ */
1199
+ async exec(command) {
1200
+ return await (0, app_1.execCommand)(command);
1201
+ }
1202
+ /**
1203
+ * Starts a command without waiting
1204
+ * for completion.
1205
+ *
1206
+ * @example
1207
+ * await visor.execDetached(
1208
+ * "notepad"
1209
+ * );
1210
+ */
1211
+ async execDetached(command) {
1212
+ await (0, app_1.execDetached)(command);
1213
+ }
1133
1214
  }
1134
1215
  exports.Visor = Visor;
1135
1216
  exports.visor = new Visor();
package/dist/matcher.js CHANGED
@@ -10,7 +10,6 @@ const fs_1 = __importDefault(require("fs"));
10
10
  const pngjs_1 = require("pngjs");
11
11
  const cv = require("@techstark/opencv-js");
12
12
  const templateCache = new Map();
13
- const sharp_1 = __importDefault(require("sharp"));
14
13
  const logger_1 = require("./logger");
15
14
  async function loadTemplate(path) {
16
15
  if (templateCache.has(path)) {
@@ -19,8 +18,7 @@ async function loadTemplate(path) {
19
18
  }
20
19
  (0, logger_1.log)(`[CACHE] Loading template: ${path}`);
21
20
  const buffer = fs_1.default.readFileSync(path);
22
- const processed = await (0, sharp_1.default)(buffer).normalize().sharpen().png().toBuffer();
23
- const png = pngjs_1.PNG.sync.read(processed);
21
+ const png = pngjs_1.PNG.sync.read(buffer);
24
22
  const template = cv.matFromImageData({
25
23
  data: png.data,
26
24
  width: png.width,
@@ -37,7 +35,8 @@ function resizeTemplate(template, scale) {
37
35
  return resized;
38
36
  }
39
37
  function matchTemplate(screen, template, confidence = 0.8) {
40
- const scales = Array.from({ length: 11 }, (_, i) => Number((0.5 + i * 0.1).toFixed(2)));
38
+ const start = Date.now();
39
+ const scales = [1.0, 0.9, 1.1, 0.8, 1.2, 0.7, 1.3, 0.6, 1.4, 0.5, 1.5];
41
40
  let bestMatch = null;
42
41
  for (const scale of scales) {
43
42
  const resizedTemplate = resizeTemplate(template, scale);
@@ -61,11 +60,13 @@ function matchTemplate(screen, template, confidence = 0.8) {
61
60
  };
62
61
  if (maxVal >= 0.98) {
63
62
  resizedTemplate.delete();
63
+ (0, logger_1.log)(`[MATCH] Total: ${Date.now() - start} ms`);
64
64
  return bestMatch;
65
65
  }
66
66
  }
67
67
  resizedTemplate.delete();
68
68
  }
69
+ (0, logger_1.log)(`[MATCH] Total: ${Date.now() - start} ms`);
69
70
  return bestMatch;
70
71
  }
71
72
  function findAllMatches(screen, template, confidence = 0.8) {
package/dist/ocr.js CHANGED
@@ -78,9 +78,3 @@ async function extractTextFromRegion(region) {
78
78
  (0, logger_1.log)(result.data.text);
79
79
  return result.data.text;
80
80
  }
81
- (async () => {
82
- const start = Date.now();
83
- const text = await extractTextFromRegion();
84
- console.log(`OCR Time: ${Date.now() - start} ms`);
85
- await terminateOCR();
86
- })();
package/dist/screen.js CHANGED
@@ -8,17 +8,16 @@ exports.saveScreenshot = saveScreenshot;
8
8
  const screenshot_desktop_1 = __importDefault(require("screenshot-desktop"));
9
9
  const pngjs_1 = require("pngjs");
10
10
  const fs_1 = __importDefault(require("fs"));
11
- const sharp_1 = __importDefault(require("sharp"));
12
11
  const cv = require("@techstark/opencv-js");
13
12
  async function captureScreen() {
14
13
  const buffer = await (0, screenshot_desktop_1.default)({ format: "png" });
15
- const processed = await (0, sharp_1.default)(buffer).normalize().sharpen().png().toBuffer();
16
- const png = pngjs_1.PNG.sync.read(processed);
17
- return cv.matFromImageData({
14
+ const png = pngjs_1.PNG.sync.read(buffer);
15
+ const mat = cv.matFromImageData({
18
16
  data: png.data,
19
17
  width: png.width,
20
18
  height: png.height,
21
19
  });
20
+ return mat;
22
21
  }
23
22
  async function saveScreenshot(path) {
24
23
  const img = await (0, screenshot_desktop_1.default)({ format: "png" });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aspiresys/visor",
3
- "version": "1.1.8",
3
+ "version": "1.1.10",
4
4
  "main": "dist/index.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "scripts": {