command-cmd 1.0.2 → 1.0.4

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 (6) hide show
  1. package/cmd.js +1640 -16
  2. package/doc.md +199 -157
  3. package/docPTBR.md +204 -157
  4. package/map.md +238 -0
  5. package/mapPTBR.md +238 -0
  6. package/package.json +5 -2
package/cmd.js CHANGED
@@ -1,3 +1,90 @@
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+
4
+ const { readFile, writeFile } = fs.promises;
5
+ const { resolve: resolvePath } = path;
6
+
7
+ const DEFAULT_APPS = Object.freeze({
8
+ spotify: {
9
+ launcher: { x: -2, y: 145 },
10
+ searchIcon: { x: 584, y: 90 }
11
+ },
12
+ chrome: {
13
+ launcher: { x: 20, y: 219 },
14
+ searchIcon: { x: 546, y: 87 }
15
+ },
16
+ youtube: {
17
+ launcher: { x: 20, y: 225 },
18
+ searchIcon: { x: 575, y: 144 }
19
+ }
20
+ });
21
+
22
+ const ACTION_ALIASES = Object.freeze({
23
+ skip_ads: 'skip_ads',
24
+ skipads: 'skip_ads',
25
+ pular_ads: 'skip_ads',
26
+ pularads: 'skip_ads',
27
+ click: 'click',
28
+ clicar: 'click',
29
+ double_click: 'double_click',
30
+ doubleclick: 'double_click',
31
+ clique_duplo: 'double_click',
32
+ cliqueduplo: 'double_click',
33
+ scroll: 'scroll',
34
+ rolar: 'scroll',
35
+ move_sequence: 'move_sequence',
36
+ movesequence: 'move_sequence',
37
+ mover_sequencia: 'move_sequence',
38
+ moversequencia: 'move_sequence',
39
+ sequencia_movimento: 'move_sequence',
40
+ sequence_move: 'move_sequence',
41
+ open_app: 'open_app',
42
+ openapp: 'open_app',
43
+ abrir_app: 'open_app',
44
+ abrirapp: 'open_app',
45
+ close_app: 'close_app',
46
+ closeapp: 'close_app',
47
+ fechar_app: 'close_app',
48
+ fecharapp: 'close_app'
49
+ });
50
+
51
+ const BUTTON_ALIASES = Object.freeze({
52
+ pularads: 'pular_ads',
53
+ pular_ads: 'pular_ads',
54
+ pular_anuncio: 'pular_ads',
55
+ pular_anuncios: 'pular_ads',
56
+ skip_ads: 'pular_ads',
57
+ skipad: 'pular_ads',
58
+ search: 'barra_pesquisa',
59
+ searchbar: 'barra_pesquisa',
60
+ search_bar: 'barra_pesquisa',
61
+ barra_pesquisa: 'barra_pesquisa',
62
+ barrapesquisa: 'barra_pesquisa',
63
+ fechar: 'fechar',
64
+ close: 'fechar',
65
+ close_app: 'fechar'
66
+ });
67
+
68
+ const APP_ALIASES = Object.freeze({
69
+ google: 'chrome',
70
+ google_chrome: 'chrome'
71
+ });
72
+
73
+ const APP_STATE = Object.freeze({
74
+ OPEN: 'aberto',
75
+ CLOSED: 'fechado'
76
+ });
77
+
78
+ const CLICK_MODE = Object.freeze({
79
+ NONE: 'none',
80
+ BETWEEN: 'between',
81
+ EACH: 'each',
82
+ FIRST: 'first',
83
+ LAST: 'last'
84
+ });
85
+
86
+ let robotCache;
87
+
1
88
  export function cmd(message) {
2
89
  if (typeof message !== 'string') {
3
90
  return [];
@@ -11,10 +98,17 @@ export function cmd(message) {
11
98
  const inside = match[1].trim();
12
99
  if (!inside) continue;
13
100
 
14
- const obj = {
101
+ const command = {
15
102
  type: undefined,
16
103
  command: undefined,
17
104
  extra: undefined,
105
+ button: undefined,
106
+ points: undefined,
107
+ path: undefined,
108
+ interval: undefined,
109
+ clickDelay: undefined,
110
+ click: undefined,
111
+ doubleClick: undefined,
18
112
  seq: undefined
19
113
  };
20
114
 
@@ -24,6 +118,13 @@ export function cmd(message) {
24
118
  const part = rawPart.trim();
25
119
  if (!part) continue;
26
120
 
121
+ if (part.indexOf(':') === -1) {
122
+ if (!command.type) {
123
+ command.type = part;
124
+ }
125
+ continue;
126
+ }
127
+
27
128
  const [rawKey, ...rest] = part.split(':');
28
129
  if (!rawKey || rest.length === 0) continue;
29
130
 
@@ -32,28 +133,75 @@ export function cmd(message) {
32
133
  if (!value) continue;
33
134
 
34
135
  if (key === 'type' || key === 'tipo') {
35
- obj.type = value;
136
+ command.type = value;
36
137
  } else if (key === 'command' || key === 'comando') {
37
- obj.command = value;
138
+ command.command = value;
38
139
  } else if (key === 'extra') {
39
- obj.extra = value;
140
+ command.extra = value;
141
+ } else if (key === 'button' || key === 'botao' || key === 'botão') {
142
+ command.button = value;
143
+ } else if (
144
+ key === 'points' ||
145
+ key === 'pontos' ||
146
+ key === 'path' ||
147
+ key === 'trajeto' ||
148
+ key === 'caminho' ||
149
+ key === 'coords' ||
150
+ key === 'coordenadas'
151
+ ) {
152
+ command.points = value;
153
+ command.path = value;
154
+ if (!command.command) {
155
+ command.command = value;
156
+ }
157
+ } else if (key === 'interval' || key === 'intervalo') {
158
+ const interval = toNonNegativeInteger(value);
159
+ if (interval !== null) {
160
+ command.interval = interval;
161
+ }
162
+ } else if (
163
+ key === 'clickdelay' ||
164
+ key === 'delayclick' ||
165
+ key === 'delay_clique' ||
166
+ key === 'delayclique' ||
167
+ key === 'click_delay' ||
168
+ key === 'delay'
169
+ ) {
170
+ const clickDelay = toNonNegativeInteger(value);
171
+ if (clickDelay !== null) {
172
+ command.clickDelay = clickDelay;
173
+ }
174
+ } else if (key === 'click' || key === 'clique') {
175
+ command.click = value;
176
+ } else if (
177
+ key === 'doubleclick' ||
178
+ key === 'double_click' ||
179
+ key === 'double' ||
180
+ key === 'duplo' ||
181
+ key === 'cliqueduplo' ||
182
+ key === 'clique_duplo'
183
+ ) {
184
+ const doubleClick = parseBooleanValue(value);
185
+ if (doubleClick !== null) {
186
+ command.doubleClick = doubleClick;
187
+ }
40
188
  } else if (key === 'seq') {
41
- const seq = Number(value);
42
- if (Number.isInteger(seq) && seq >= 0) {
43
- obj.seq = seq;
189
+ const seq = toNonNegativeInteger(value);
190
+ if (seq !== null) {
191
+ command.seq = seq;
44
192
  }
45
- } else if (!obj.type) {
46
- obj.type = rawKey.trim();
47
- obj.command = value;
48
- } else if (!obj.command) {
49
- obj.command = value;
50
- } else if (!obj.extra) {
51
- obj.extra = value;
193
+ } else if (!command.type) {
194
+ command.type = rawKey.trim();
195
+ command.command = value;
196
+ } else if (!command.command) {
197
+ command.command = value;
198
+ } else if (!command.extra) {
199
+ command.extra = value;
52
200
  }
53
201
  }
54
202
 
55
- if (obj.type && obj.command) {
56
- results.push(obj);
203
+ if (command.type && command.command) {
204
+ results.push(command);
57
205
  }
58
206
  }
59
207
 
@@ -64,3 +212,1479 @@ export function extractFirstCommand(message) {
64
212
  const [first] = cmd(message);
65
213
  return first || null;
66
214
  }
215
+
216
+ export async function cursor(input, options = {}) {
217
+ const commands = normalizeCursorInput(input);
218
+ if (commands.length === 0) {
219
+ return {
220
+ executed: [],
221
+ skipped: [{ reason: 'no_commands' }],
222
+ totalCommands: 0,
223
+ totalExecutions: 0
224
+ };
225
+ }
226
+
227
+ const runtime = createCursorRuntime(options);
228
+ let actions;
229
+ const executed = [];
230
+ const skipped = [];
231
+
232
+ async function getActions() {
233
+ if (actions) {
234
+ return actions;
235
+ }
236
+ const robot = await getRobot();
237
+ actions = createActionHandlers(robot, runtime);
238
+ return actions;
239
+ }
240
+
241
+ for (let commandIndex = 0; commandIndex < commands.length; commandIndex++) {
242
+ const command = commands[commandIndex];
243
+ const actionKey = resolveActionKey(command.type);
244
+
245
+ if (!actionKey) {
246
+ skipped.push({
247
+ commandIndex,
248
+ type: command.type,
249
+ command: command.command,
250
+ reason: 'unknown_type'
251
+ });
252
+ continue;
253
+ }
254
+
255
+ const repeatCount = getRepeatCount(command.seq);
256
+ if (repeatCount === 0) {
257
+ skipped.push({
258
+ commandIndex,
259
+ type: command.type,
260
+ command: command.command,
261
+ reason: 'seq_zero'
262
+ });
263
+ continue;
264
+ }
265
+
266
+ if (isMapAwareAction(actionKey)) {
267
+ try {
268
+ await runtime.getMapStore();
269
+ } catch (error) {
270
+ skipped.push({
271
+ commandIndex,
272
+ type: command.type,
273
+ command: command.command,
274
+ reason: 'map_unavailable',
275
+ detail: error instanceof Error ? error.message : String(error)
276
+ });
277
+ continue;
278
+ }
279
+ }
280
+
281
+ let action;
282
+ try {
283
+ const actionHandlers = await getActions();
284
+ action = actionHandlers[actionKey];
285
+ } catch (error) {
286
+ skipped.push({
287
+ commandIndex,
288
+ type: command.type,
289
+ command: command.command,
290
+ reason: 'robot_unavailable',
291
+ detail: error instanceof Error ? error.message : String(error)
292
+ });
293
+ continue;
294
+ }
295
+
296
+ if (!action) {
297
+ skipped.push({
298
+ commandIndex,
299
+ type: command.type,
300
+ command: command.command,
301
+ reason: 'unknown_type'
302
+ });
303
+ continue;
304
+ }
305
+
306
+ for (let iteration = 1; iteration <= repeatCount; iteration++) {
307
+ try {
308
+ await action(command);
309
+ executed.push({
310
+ commandIndex,
311
+ iteration,
312
+ type: command.type,
313
+ command: command.command,
314
+ extra: command.extra,
315
+ button: command.button,
316
+ points: command.points,
317
+ path: command.path,
318
+ interval: command.interval,
319
+ clickDelay: command.clickDelay,
320
+ click: command.click,
321
+ doubleClick: command.doubleClick,
322
+ seq: command.seq
323
+ });
324
+ } catch (error) {
325
+ skipped.push({
326
+ commandIndex,
327
+ iteration,
328
+ type: command.type,
329
+ command: command.command,
330
+ reason: 'action_error',
331
+ detail: error instanceof Error ? error.message : String(error)
332
+ });
333
+ break;
334
+ }
335
+ }
336
+ }
337
+
338
+ return {
339
+ executed,
340
+ skipped,
341
+ totalCommands: commands.length,
342
+ totalExecutions: executed.length
343
+ };
344
+ }
345
+
346
+ function createCursorRuntime(options) {
347
+ const config = buildCursorConfig(options);
348
+ let mapStorePromise;
349
+
350
+ return {
351
+ config,
352
+ async getMapStore() {
353
+ if (mapStorePromise) {
354
+ return mapStorePromise;
355
+ }
356
+ mapStorePromise = createMapStore(config);
357
+ return mapStorePromise;
358
+ }
359
+ };
360
+ }
361
+
362
+ function buildCursorConfig(options) {
363
+ const raw = options && typeof options === 'object' ? options : {};
364
+ const mapPath =
365
+ typeof raw.mapPath === 'string' && raw.mapPath.trim()
366
+ ? raw.mapPath.trim()
367
+ : 'map.json';
368
+
369
+ return {
370
+ mapPath: resolvePath(mapPath),
371
+ persistMap: raw.persistMap !== false,
372
+ createMapIfMissing: raw.createMapIfMissing !== false,
373
+ apps: mergeOptionApps(raw.apps),
374
+ launchKey:
375
+ raw.launchKey === false || raw.launchKey === null
376
+ ? undefined
377
+ : typeof raw.launchKey === 'string' && raw.launchKey.trim()
378
+ ? raw.launchKey.trim()
379
+ : 'command',
380
+ typeDelay: parseIntegerOption(raw.typeDelay, 50, 0),
381
+ moveSteps: parseIntegerOption(raw.moveSteps, 50, 1),
382
+ moveDelay: parseIntegerOption(raw.moveDelay, 8, 0),
383
+ scrollSteps: parseIntegerOption(raw.scrollSteps, 10, 1),
384
+ scrollDelay: parseIntegerOption(raw.scrollDelay, 20, 0),
385
+ sequenceInterval: parseIntegerOption(raw.sequenceInterval, 250, 0),
386
+ sequenceClickMode: normalizeClickMode(raw.sequenceClick, CLICK_MODE.NONE),
387
+ sequenceDoubleClick:
388
+ parseBooleanValue(raw.sequenceDoubleClick) === true,
389
+ defaultClosePoint: parsePoint(raw.defaultClosePoint) || { x: 1888, y: 16 },
390
+ skipAdsPoint: parsePoint(raw.skipAdsPoint) || { x: 808, y: 569 }
391
+ };
392
+ }
393
+
394
+ function mergeOptionApps(customApps) {
395
+ const merged = {};
396
+
397
+ for (const [appName, appConfig] of Object.entries(DEFAULT_APPS)) {
398
+ merged[appName] = cloneAppMap(appConfig);
399
+ }
400
+
401
+ if (!customApps || typeof customApps !== 'object') {
402
+ return merged;
403
+ }
404
+
405
+ for (const [rawName, rawConfig] of Object.entries(customApps)) {
406
+ const appName = resolveAppName(rawName);
407
+ const normalized = normalizeAppConfig(rawConfig);
408
+ if (!normalized) continue;
409
+
410
+ const current = merged[appName] || {
411
+ launcher: undefined,
412
+ searchIcon: undefined,
413
+ state: APP_STATE.CLOSED,
414
+ buttons: {}
415
+ };
416
+
417
+ merged[appName] = {
418
+ launcher: normalized.launcher || current.launcher,
419
+ searchIcon: normalized.searchIcon || current.searchIcon,
420
+ state: normalized.state || current.state || APP_STATE.CLOSED,
421
+ buttons: {
422
+ ...(current.buttons || {}),
423
+ ...(normalized.buttons || {})
424
+ }
425
+ };
426
+ }
427
+
428
+ return merged;
429
+ }
430
+
431
+ function normalizeCommandObject(value) {
432
+ if (!value || typeof value !== 'object') {
433
+ return null;
434
+ }
435
+
436
+ const rawType = value.type !== undefined ? value.type : value.tipo;
437
+ const rawCommand = value.command !== undefined ? value.command : value.comando;
438
+ const rawButton =
439
+ value.button !== undefined
440
+ ? value.button
441
+ : value.botao !== undefined
442
+ ? value.botao
443
+ : value['botão'];
444
+ const rawPoints =
445
+ value.points !== undefined
446
+ ? value.points
447
+ : value.pontos !== undefined
448
+ ? value.pontos
449
+ : value.path !== undefined
450
+ ? value.path
451
+ : value.trajeto !== undefined
452
+ ? value.trajeto
453
+ : value.caminho !== undefined
454
+ ? value.caminho
455
+ : value.coords !== undefined
456
+ ? value.coords
457
+ : value.coordenadas;
458
+ const rawPath =
459
+ value.path !== undefined
460
+ ? value.path
461
+ : value.caminho !== undefined
462
+ ? value.caminho
463
+ : value.trajeto;
464
+ const rawInterval =
465
+ value.interval !== undefined ? value.interval : value.intervalo;
466
+ const rawClickDelay =
467
+ value.clickDelay !== undefined
468
+ ? value.clickDelay
469
+ : value.delayClick !== undefined
470
+ ? value.delayClick
471
+ : value.delayClique !== undefined
472
+ ? value.delayClique
473
+ : value.delay;
474
+ const rawClick = value.click !== undefined ? value.click : value.clique;
475
+ const rawDoubleClick =
476
+ value.doubleClick !== undefined
477
+ ? value.doubleClick
478
+ : value.double_click !== undefined
479
+ ? value.double_click
480
+ : value.duplo !== undefined
481
+ ? value.duplo
482
+ : value.cliqueDuplo !== undefined
483
+ ? value.cliqueDuplo
484
+ : value.clique_duplo;
485
+
486
+ const type =
487
+ typeof rawType === 'string' ? rawType.trim() : String(rawType || '').trim();
488
+
489
+ const normalizedPoints = normalizePointsValue(
490
+ rawPoints !== undefined ? rawPoints : rawPath
491
+ );
492
+
493
+ let command =
494
+ typeof rawCommand === 'string'
495
+ ? rawCommand.trim()
496
+ : Number.isFinite(rawCommand)
497
+ ? String(rawCommand)
498
+ : '';
499
+
500
+ if (!command && normalizedPoints) {
501
+ command = serializePoints(normalizedPoints);
502
+ }
503
+
504
+ if (!type || !command) {
505
+ return null;
506
+ }
507
+
508
+ const normalized = {
509
+ type,
510
+ command,
511
+ extra:
512
+ value.extra === undefined || value.extra === null
513
+ ? undefined
514
+ : String(value.extra),
515
+ button: undefined,
516
+ points: undefined,
517
+ path: undefined,
518
+ interval: undefined,
519
+ clickDelay: undefined,
520
+ click: undefined,
521
+ doubleClick: undefined,
522
+ seq: undefined
523
+ };
524
+
525
+ if (rawButton !== undefined && rawButton !== null) {
526
+ const buttonText = String(rawButton).trim();
527
+ if (buttonText) {
528
+ normalized.button = buttonText;
529
+ }
530
+ }
531
+
532
+ if (normalizedPoints) {
533
+ normalized.points = normalizedPoints;
534
+ normalized.path = normalizedPoints;
535
+ }
536
+
537
+ const interval = toNonNegativeInteger(rawInterval);
538
+ if (interval !== null) {
539
+ normalized.interval = interval;
540
+ }
541
+
542
+ const clickDelay = toNonNegativeInteger(rawClickDelay);
543
+ if (clickDelay !== null) {
544
+ normalized.clickDelay = clickDelay;
545
+ }
546
+
547
+ if (rawClick !== undefined && rawClick !== null) {
548
+ const clickMode = String(rawClick).trim();
549
+ if (clickMode) {
550
+ normalized.click = clickMode;
551
+ }
552
+ }
553
+
554
+ const doubleClick = parseBooleanValue(rawDoubleClick);
555
+ if (doubleClick !== null) {
556
+ normalized.doubleClick = doubleClick;
557
+ }
558
+
559
+ const rawSeq = value.seq !== undefined ? value.seq : value.sequencia;
560
+ const seq = toNonNegativeInteger(rawSeq);
561
+ if (seq !== null) {
562
+ normalized.seq = seq;
563
+ }
564
+
565
+ return normalized;
566
+ }
567
+
568
+ function normalizeCursorInput(input) {
569
+ if (typeof input === 'string') {
570
+ return cmd(input);
571
+ }
572
+
573
+ if (Array.isArray(input)) {
574
+ return input.map(normalizeCommandObject).filter(Boolean);
575
+ }
576
+
577
+ const single = normalizeCommandObject(input);
578
+ return single ? [single] : [];
579
+ }
580
+
581
+ function getRepeatCount(seq) {
582
+ const parsed = toNonNegativeInteger(seq);
583
+ if (parsed === null) {
584
+ return 1;
585
+ }
586
+ return parsed;
587
+ }
588
+
589
+ function toNonNegativeInteger(value) {
590
+ const parsed = Number(value);
591
+ if (!Number.isInteger(parsed) || parsed < 0) {
592
+ return null;
593
+ }
594
+ return parsed;
595
+ }
596
+
597
+ function parseIntegerOption(value, fallback, min) {
598
+ const parsed = Number(value);
599
+ if (!Number.isInteger(parsed) || parsed < min) {
600
+ return fallback;
601
+ }
602
+ return parsed;
603
+ }
604
+
605
+ function parseBooleanValue(value) {
606
+ if (value === undefined || value === null) {
607
+ return null;
608
+ }
609
+
610
+ if (typeof value === 'boolean') {
611
+ return value;
612
+ }
613
+
614
+ const token = canonicalToken(value);
615
+ if (
616
+ token === 'true' ||
617
+ token === '1' ||
618
+ token === 'sim' ||
619
+ token === 'yes' ||
620
+ token === 'on'
621
+ ) {
622
+ return true;
623
+ }
624
+
625
+ if (
626
+ token === 'false' ||
627
+ token === '0' ||
628
+ token === 'nao' ||
629
+ token === 'no' ||
630
+ token === 'off'
631
+ ) {
632
+ return false;
633
+ }
634
+
635
+ return null;
636
+ }
637
+
638
+ function normalizeClickMode(value, fallback) {
639
+ if (value === true) {
640
+ return CLICK_MODE.BETWEEN;
641
+ }
642
+
643
+ if (value === false) {
644
+ return CLICK_MODE.NONE;
645
+ }
646
+
647
+ const token = canonicalToken(value);
648
+
649
+ if (
650
+ token === CLICK_MODE.EACH ||
651
+ token === 'cada' ||
652
+ token === 'todos' ||
653
+ token === 'todas'
654
+ ) {
655
+ return CLICK_MODE.EACH;
656
+ }
657
+
658
+ if (token === CLICK_MODE.BETWEEN || token === 'entre') {
659
+ return CLICK_MODE.BETWEEN;
660
+ }
661
+
662
+ if (token === CLICK_MODE.FIRST || token === 'primeiro') {
663
+ return CLICK_MODE.FIRST;
664
+ }
665
+
666
+ if (token === CLICK_MODE.LAST || token === 'ultimo' || token === 'final') {
667
+ return CLICK_MODE.LAST;
668
+ }
669
+
670
+ if (
671
+ token === CLICK_MODE.NONE ||
672
+ token === 'sem' ||
673
+ token === 'nenhum' ||
674
+ token === 'desligado'
675
+ ) {
676
+ return CLICK_MODE.NONE;
677
+ }
678
+
679
+ return fallback || CLICK_MODE.NONE;
680
+ }
681
+
682
+ function parsePointToken(token) {
683
+ const value = String(token || '').trim();
684
+ if (!value) return null;
685
+
686
+ const match = value.match(/^(-?\d+)\s*(?:x|:|,)\s*(-?\d+)$/i);
687
+ if (!match) return null;
688
+
689
+ return {
690
+ x: Number(match[1]),
691
+ y: Number(match[2])
692
+ };
693
+ }
694
+
695
+ function normalizePointsValue(value) {
696
+ if (value === undefined || value === null) {
697
+ return undefined;
698
+ }
699
+
700
+ if (Array.isArray(value)) {
701
+ const points = [];
702
+ for (const item of value) {
703
+ const point = parsePoint(item) || parsePointToken(item);
704
+ if (!point) {
705
+ return undefined;
706
+ }
707
+ points.push(point);
708
+ }
709
+ return points.length > 0 ? points : undefined;
710
+ }
711
+
712
+ if (typeof value === 'string') {
713
+ const parts = value
714
+ .split(/[|;>]/g)
715
+ .map((part) => part.trim())
716
+ .filter(Boolean);
717
+
718
+ if (parts.length === 0) {
719
+ return undefined;
720
+ }
721
+
722
+ const points = [];
723
+ for (const part of parts) {
724
+ const point = parsePointToken(part);
725
+ if (!point) {
726
+ return undefined;
727
+ }
728
+ points.push(point);
729
+ }
730
+ return points.length > 0 ? points : undefined;
731
+ }
732
+
733
+ const singlePoint = parsePoint(value);
734
+ if (singlePoint) {
735
+ return [singlePoint];
736
+ }
737
+
738
+ return undefined;
739
+ }
740
+
741
+ function serializePoints(points) {
742
+ if (!Array.isArray(points) || points.length === 0) {
743
+ return '';
744
+ }
745
+
746
+ return points.map((point) => `${point.x}x${point.y}`).join('|');
747
+ }
748
+
749
+ function normalizePathConfig(rawPath) {
750
+ if (rawPath === undefined || rawPath === null) {
751
+ return undefined;
752
+ }
753
+
754
+ let points;
755
+ let clickMode = CLICK_MODE.NONE;
756
+ let interval = 0;
757
+ let clickDelay = 0;
758
+ let doubleClick = false;
759
+
760
+ if (typeof rawPath === 'string' || Array.isArray(rawPath)) {
761
+ points = normalizePointsValue(rawPath);
762
+ } else if (typeof rawPath === 'object') {
763
+ points = normalizePointsValue(
764
+ rawPath.points !== undefined
765
+ ? rawPath.points
766
+ : rawPath.pontos !== undefined
767
+ ? rawPath.pontos
768
+ : rawPath.path !== undefined
769
+ ? rawPath.path
770
+ : rawPath.caminho !== undefined
771
+ ? rawPath.caminho
772
+ : rawPath.coords !== undefined
773
+ ? rawPath.coords
774
+ : rawPath.coordenadas
775
+ );
776
+
777
+ clickMode = normalizeClickMode(
778
+ rawPath.click !== undefined ? rawPath.click : rawPath.clique,
779
+ CLICK_MODE.NONE
780
+ );
781
+
782
+ interval =
783
+ toNonNegativeInteger(
784
+ rawPath.interval !== undefined ? rawPath.interval : rawPath.intervalo
785
+ ) || 0;
786
+
787
+ clickDelay =
788
+ toNonNegativeInteger(
789
+ rawPath.clickDelay !== undefined
790
+ ? rawPath.clickDelay
791
+ : rawPath.delayClick !== undefined
792
+ ? rawPath.delayClick
793
+ : rawPath.delayClique !== undefined
794
+ ? rawPath.delayClique
795
+ : rawPath.delay
796
+ ) || 0;
797
+
798
+ doubleClick =
799
+ parseBooleanValue(
800
+ rawPath.doubleClick !== undefined
801
+ ? rawPath.doubleClick
802
+ : rawPath.double_click !== undefined
803
+ ? rawPath.double_click
804
+ : rawPath.duplo
805
+ ) === true;
806
+ } else {
807
+ return undefined;
808
+ }
809
+
810
+ if (!points || points.length === 0) {
811
+ return undefined;
812
+ }
813
+
814
+ return {
815
+ points,
816
+ clickMode,
817
+ interval,
818
+ clickDelay,
819
+ doubleClick
820
+ };
821
+ }
822
+
823
+ function normalizeTargetConfig(rawTarget) {
824
+ if (rawTarget === undefined || rawTarget === null) {
825
+ return undefined;
826
+ }
827
+
828
+ if (typeof rawTarget !== 'object') {
829
+ const directPoint = parsePoint(rawTarget);
830
+ return directPoint ? { position: directPoint } : undefined;
831
+ }
832
+
833
+ let position =
834
+ parsePoint(
835
+ rawTarget.position !== undefined
836
+ ? rawTarget.position
837
+ : rawTarget.posicao !== undefined
838
+ ? rawTarget.posicao
839
+ : rawTarget.destino !== undefined
840
+ ? rawTarget.destino
841
+ : rawTarget.target !== undefined
842
+ ? rawTarget.target
843
+ : rawTarget
844
+ ) || undefined;
845
+
846
+ const pathConfig = normalizePathConfig(
847
+ rawTarget.caminho !== undefined ? rawTarget.caminho : rawTarget.path
848
+ );
849
+
850
+ if (!position && pathConfig && pathConfig.points.length > 0) {
851
+ const lastPoint = pathConfig.points[pathConfig.points.length - 1];
852
+ position = { x: lastPoint.x, y: lastPoint.y };
853
+ }
854
+
855
+ if (!position) {
856
+ return undefined;
857
+ }
858
+
859
+ return {
860
+ position,
861
+ ...(pathConfig ? { path: pathConfig } : {})
862
+ };
863
+ }
864
+
865
+ function shouldClickOnStep(mode, index, totalPoints) {
866
+ if (mode === CLICK_MODE.EACH) {
867
+ return true;
868
+ }
869
+
870
+ if (mode === CLICK_MODE.BETWEEN) {
871
+ return index < totalPoints - 1;
872
+ }
873
+
874
+ if (mode === CLICK_MODE.FIRST) {
875
+ return index === 0;
876
+ }
877
+
878
+ if (mode === CLICK_MODE.LAST) {
879
+ return index === totalPoints - 1;
880
+ }
881
+
882
+ return false;
883
+ }
884
+
885
+ function removeDiacritics(value) {
886
+ return String(value || '')
887
+ .normalize('NFD')
888
+ .replace(/[\u0300-\u036f]/g, '');
889
+ }
890
+
891
+ function canonicalToken(value) {
892
+ return removeDiacritics(value)
893
+ .trim()
894
+ .toLowerCase()
895
+ .replace(/[^a-z0-9_\-\s]/g, '')
896
+ .replace(/[-\s]+/g, '_');
897
+ }
898
+
899
+ function resolveActionKey(type) {
900
+ const key = canonicalToken(type);
901
+ return ACTION_ALIASES[key];
902
+ }
903
+
904
+ function isMapAwareAction(actionKey) {
905
+ return (
906
+ actionKey === 'open_app' ||
907
+ actionKey === 'close_app' ||
908
+ actionKey === 'skip_ads'
909
+ );
910
+ }
911
+
912
+ function resolveAppName(name) {
913
+ const key = canonicalToken(name);
914
+ return APP_ALIASES[key] || key;
915
+ }
916
+
917
+ function resolveButtonKey(name) {
918
+ const key = canonicalToken(name);
919
+ return BUTTON_ALIASES[key] || key;
920
+ }
921
+
922
+ function normalizeState(value) {
923
+ const key = canonicalToken(value);
924
+ if (key === 'aberto' || key === 'open' || key === 'opened') {
925
+ return APP_STATE.OPEN;
926
+ }
927
+ if (key === 'fechado' || key === 'closed') {
928
+ return APP_STATE.CLOSED;
929
+ }
930
+ return undefined;
931
+ }
932
+
933
+ function parsePoint(value) {
934
+ if (Array.isArray(value) && value.length === 2) {
935
+ const x = Number(value[0]);
936
+ const y = Number(value[1]);
937
+ if (Number.isFinite(x) && Number.isFinite(y)) {
938
+ return { x, y };
939
+ }
940
+ }
941
+
942
+ if (value && typeof value === 'object') {
943
+ const x = Number(value.x);
944
+ const y = Number(value.y);
945
+ if (Number.isFinite(x) && Number.isFinite(y)) {
946
+ return { x, y };
947
+ }
948
+ }
949
+
950
+ return null;
951
+ }
952
+
953
+ function clonePoint(value) {
954
+ if (!value) return undefined;
955
+ return { x: value.x, y: value.y };
956
+ }
957
+
958
+ function clonePathConfig(pathConfig) {
959
+ if (!pathConfig || typeof pathConfig !== 'object') {
960
+ return undefined;
961
+ }
962
+
963
+ return {
964
+ points: (pathConfig.points || []).map((point) => clonePoint(point)),
965
+ clickMode: pathConfig.clickMode || CLICK_MODE.NONE,
966
+ interval: toNonNegativeInteger(pathConfig.interval) || 0,
967
+ clickDelay: toNonNegativeInteger(pathConfig.clickDelay) || 0,
968
+ doubleClick: pathConfig.doubleClick === true
969
+ };
970
+ }
971
+
972
+ function cloneTargetConfig(targetConfig) {
973
+ if (!targetConfig || typeof targetConfig !== 'object') {
974
+ return undefined;
975
+ }
976
+
977
+ const position = targetConfig.position
978
+ ? clonePoint(targetConfig.position)
979
+ : clonePoint(parsePoint(targetConfig));
980
+
981
+ if (!position) {
982
+ return undefined;
983
+ }
984
+
985
+ return {
986
+ position,
987
+ ...(targetConfig.path ? { path: clonePathConfig(targetConfig.path) } : {})
988
+ };
989
+ }
990
+
991
+ function cloneButtonMap(buttons) {
992
+ const cloned = {};
993
+ if (!buttons || typeof buttons !== 'object') {
994
+ return cloned;
995
+ }
996
+
997
+ for (const [buttonName, buttonConfig] of Object.entries(buttons)) {
998
+ if (!buttonConfig || typeof buttonConfig !== 'object') continue;
999
+ if (!buttonConfig.position) continue;
1000
+ cloned[buttonName] = {
1001
+ position: clonePoint(buttonConfig.position),
1002
+ ...(buttonConfig.path ? { path: clonePathConfig(buttonConfig.path) } : {}),
1003
+ ...(buttonConfig.setState ? { setState: buttonConfig.setState } : {})
1004
+ };
1005
+ }
1006
+
1007
+ return cloned;
1008
+ }
1009
+
1010
+ function cloneAppMap(app) {
1011
+ return {
1012
+ launcher: cloneTargetConfig(app.launcher),
1013
+ searchIcon: cloneTargetConfig(app.searchIcon),
1014
+ state: app.state || APP_STATE.CLOSED,
1015
+ buttons: cloneButtonMap(app.buttons)
1016
+ };
1017
+ }
1018
+
1019
+ function normalizeButtonConfig(rawConfig) {
1020
+ if (rawConfig === undefined || rawConfig === null) {
1021
+ return null;
1022
+ }
1023
+
1024
+ const target = normalizeTargetConfig(rawConfig);
1025
+ if (!target) {
1026
+ return null;
1027
+ }
1028
+
1029
+ const rawState =
1030
+ rawConfig && typeof rawConfig === 'object'
1031
+ ? rawConfig.setState !== undefined
1032
+ ? rawConfig.setState
1033
+ : rawConfig.stateAfter !== undefined
1034
+ ? rawConfig.stateAfter
1035
+ : rawConfig.estadoDepois !== undefined
1036
+ ? rawConfig.estadoDepois
1037
+ : undefined
1038
+ : undefined;
1039
+
1040
+ const state = normalizeState(rawState);
1041
+
1042
+ return {
1043
+ position: target.position,
1044
+ ...(target.path ? { path: target.path } : {}),
1045
+ ...(state ? { setState: state } : {})
1046
+ };
1047
+ }
1048
+
1049
+ function mergeTargetPath(target, rawPath) {
1050
+ if (!target || target.path) {
1051
+ return target;
1052
+ }
1053
+
1054
+ const pathConfig = normalizePathConfig(rawPath);
1055
+ if (!pathConfig) {
1056
+ return target;
1057
+ }
1058
+
1059
+ return {
1060
+ ...target,
1061
+ path: pathConfig
1062
+ };
1063
+ }
1064
+
1065
+ function normalizeButtons(rawButtons) {
1066
+ const buttons = {};
1067
+ if (!rawButtons || typeof rawButtons !== 'object') {
1068
+ return buttons;
1069
+ }
1070
+
1071
+ for (const [rawName, rawConfig] of Object.entries(rawButtons)) {
1072
+ const button = normalizeButtonConfig(rawConfig);
1073
+ if (!button) continue;
1074
+
1075
+ const buttonName = resolveButtonKey(rawName);
1076
+ buttons[buttonName] = button;
1077
+ }
1078
+
1079
+ return buttons;
1080
+ }
1081
+
1082
+ function normalizeAppConfig(rawConfig) {
1083
+ if (!rawConfig || typeof rawConfig !== 'object') {
1084
+ return null;
1085
+ }
1086
+
1087
+ const rawLauncher =
1088
+ rawConfig.launcher !== undefined
1089
+ ? rawConfig.launcher
1090
+ : rawConfig.position !== undefined
1091
+ ? rawConfig.position
1092
+ : rawConfig.posicao;
1093
+
1094
+ let launcher = normalizeTargetConfig(rawLauncher);
1095
+ launcher = mergeTargetPath(
1096
+ launcher,
1097
+ rawConfig.caminho !== undefined ? rawConfig.caminho : rawConfig.path
1098
+ );
1099
+
1100
+ const rawSearchIcon =
1101
+ rawConfig.searchIcon !== undefined
1102
+ ? rawConfig.searchIcon
1103
+ : rawConfig.lupa !== undefined
1104
+ ? rawConfig.lupa
1105
+ : rawConfig.search;
1106
+
1107
+ const searchIcon = normalizeTargetConfig(rawSearchIcon);
1108
+
1109
+ const state = normalizeState(
1110
+ rawConfig.state !== undefined ? rawConfig.state : rawConfig.estado
1111
+ );
1112
+
1113
+ const buttons = normalizeButtons(
1114
+ rawConfig.buttons !== undefined ? rawConfig.buttons : rawConfig.botoes
1115
+ );
1116
+
1117
+ return {
1118
+ ...(launcher ? { launcher } : {}),
1119
+ ...(searchIcon ? { searchIcon } : {}),
1120
+ ...(state ? { state } : {}),
1121
+ buttons
1122
+ };
1123
+ }
1124
+
1125
+ function buildFallbackMap(config) {
1126
+ const apps = {};
1127
+
1128
+ for (const [appName, appConfig] of Object.entries(config.apps)) {
1129
+ const normalizedApp = cloneAppMap({
1130
+ ...appConfig,
1131
+ state: appConfig.state || APP_STATE.CLOSED
1132
+ });
1133
+ const buttons = normalizedApp.buttons || {};
1134
+ if (!buttons.fechar) {
1135
+ buttons.fechar = {
1136
+ position: clonePoint(config.defaultClosePoint),
1137
+ setState: APP_STATE.CLOSED
1138
+ };
1139
+ }
1140
+ normalizedApp.buttons = buttons;
1141
+ apps[appName] = normalizedApp;
1142
+ }
1143
+
1144
+ if (apps.youtube) {
1145
+ const buttons = apps.youtube.buttons || {};
1146
+ if (!buttons.pular_ads) {
1147
+ buttons.pular_ads = {
1148
+ position: clonePoint(config.skipAdsPoint)
1149
+ };
1150
+ }
1151
+ apps.youtube.buttons = buttons;
1152
+ }
1153
+
1154
+ return {
1155
+ version: 1,
1156
+ apps
1157
+ };
1158
+ }
1159
+
1160
+ function normalizeMapObject(rawMap) {
1161
+ const result = {
1162
+ version: 1,
1163
+ apps: {}
1164
+ };
1165
+
1166
+ if (!rawMap || typeof rawMap !== 'object') {
1167
+ return result;
1168
+ }
1169
+
1170
+ if (Number.isFinite(rawMap.version)) {
1171
+ result.version = Number(rawMap.version);
1172
+ }
1173
+
1174
+ const rawApps =
1175
+ rawMap.apps !== undefined
1176
+ ? rawMap.apps
1177
+ : rawMap.aplicativos !== undefined
1178
+ ? rawMap.aplicativos
1179
+ : undefined;
1180
+
1181
+ if (!rawApps || typeof rawApps !== 'object') {
1182
+ return result;
1183
+ }
1184
+
1185
+ for (const [rawAppName, rawAppConfig] of Object.entries(rawApps)) {
1186
+ const appName = resolveAppName(rawAppName);
1187
+ const appConfig = normalizeAppConfig(rawAppConfig);
1188
+ if (!appConfig) continue;
1189
+
1190
+ result.apps[appName] = {
1191
+ launcher: appConfig.launcher,
1192
+ searchIcon: appConfig.searchIcon,
1193
+ state: appConfig.state || APP_STATE.CLOSED,
1194
+ buttons: appConfig.buttons || {}
1195
+ };
1196
+ }
1197
+
1198
+ return result;
1199
+ }
1200
+
1201
+ function mergeMaps(baseMap, overrideMap) {
1202
+ const merged = {
1203
+ version:
1204
+ overrideMap && Number.isFinite(overrideMap.version)
1205
+ ? overrideMap.version
1206
+ : baseMap.version,
1207
+ apps: {}
1208
+ };
1209
+
1210
+ for (const [appName, appConfig] of Object.entries(baseMap.apps || {})) {
1211
+ merged.apps[appName] = cloneAppMap(appConfig);
1212
+ }
1213
+
1214
+ for (const [appName, appConfig] of Object.entries(overrideMap.apps || {})) {
1215
+ const current = merged.apps[appName] || {
1216
+ launcher: undefined,
1217
+ searchIcon: undefined,
1218
+ state: APP_STATE.CLOSED,
1219
+ buttons: {}
1220
+ };
1221
+
1222
+ merged.apps[appName] = {
1223
+ launcher: appConfig.launcher || current.launcher,
1224
+ searchIcon: appConfig.searchIcon || current.searchIcon,
1225
+ state: appConfig.state || current.state || APP_STATE.CLOSED,
1226
+ buttons: {
1227
+ ...(current.buttons || {}),
1228
+ ...(appConfig.buttons || {})
1229
+ }
1230
+ };
1231
+ }
1232
+
1233
+ return merged;
1234
+ }
1235
+
1236
+ async function createMapStore(config) {
1237
+ const fallbackMap = buildFallbackMap(config);
1238
+ let fromFileMap = null;
1239
+ let mapCreatedFromMissing = false;
1240
+
1241
+ try {
1242
+ const content = await readFile(config.mapPath, 'utf8');
1243
+ let parsed;
1244
+ try {
1245
+ parsed = JSON.parse(content);
1246
+ } catch (error) {
1247
+ throw new Error(`invalid_map_json:${config.mapPath}`);
1248
+ }
1249
+ fromFileMap = normalizeMapObject(parsed);
1250
+ } catch (error) {
1251
+ if (error && error.code === 'ENOENT') {
1252
+ if (!config.createMapIfMissing) {
1253
+ throw new Error(
1254
+ `map_not_found:${config.mapPath}. Crie o arquivo map.json ou use { createMapIfMissing: true }.`
1255
+ );
1256
+ }
1257
+ fromFileMap = { version: 1, apps: {} };
1258
+ mapCreatedFromMissing = true;
1259
+ } else if (error instanceof Error) {
1260
+ throw error;
1261
+ } else {
1262
+ throw new Error(`map_read_error:${config.mapPath}`);
1263
+ }
1264
+ }
1265
+
1266
+ const map = mergeMaps(fallbackMap, fromFileMap);
1267
+ const store = {
1268
+ path: config.mapPath,
1269
+ persistMap: config.persistMap,
1270
+ map
1271
+ };
1272
+
1273
+ if (config.createMapIfMissing && mapCreatedFromMissing) {
1274
+ await persistMapStore(store);
1275
+ }
1276
+
1277
+ return store;
1278
+ }
1279
+
1280
+ function resolveAppInMap(store, rawAppName) {
1281
+ const appName = resolveAppName(rawAppName);
1282
+ return {
1283
+ appName,
1284
+ app: store.map.apps[appName]
1285
+ };
1286
+ }
1287
+
1288
+ function resolveMappedButton(appConfig, rawButtonName) {
1289
+ if (!rawButtonName) return null;
1290
+ const buttonName = resolveButtonKey(rawButtonName);
1291
+ return appConfig.buttons && appConfig.buttons[buttonName]
1292
+ ? appConfig.buttons[buttonName]
1293
+ : null;
1294
+ }
1295
+
1296
+ async function updateAppState(store, appName, nextState) {
1297
+ const state = normalizeState(nextState);
1298
+ if (!state) return;
1299
+
1300
+ const app = store.map.apps[appName];
1301
+ if (!app) return;
1302
+ if (app.state === state) return;
1303
+
1304
+ app.state = state;
1305
+ await persistMapStore(store);
1306
+ }
1307
+
1308
+ async function persistMapStore(store) {
1309
+ if (!store.persistMap) return;
1310
+ const serialized = JSON.stringify(store.map, null, 2) + '\n';
1311
+ await writeFile(store.path, serialized, 'utf8');
1312
+ }
1313
+
1314
+ function getAppState(appConfig) {
1315
+ return normalizeState(appConfig && appConfig.state) || APP_STATE.CLOSED;
1316
+ }
1317
+
1318
+ function sleep(ms) {
1319
+ return new Promise((resolve) => setTimeout(resolve, ms));
1320
+ }
1321
+
1322
+ async function smoothMove(robot, xFinal, yFinal, steps, delay) {
1323
+ const current = robot.getMousePos();
1324
+ const totalSteps = Math.max(1, parseIntegerOption(steps, 50, 1));
1325
+
1326
+ for (let i = 1; i <= totalSteps; i++) {
1327
+ const progress = i / totalSteps;
1328
+ const x = Math.round(current.x + (xFinal - current.x) * progress);
1329
+ const y = Math.round(current.y + (yFinal - current.y) * progress);
1330
+ robot.moveMouse(x, y);
1331
+ await sleep(delay);
1332
+ }
1333
+ }
1334
+
1335
+ async function smoothScroll(robot, amount, steps, delay) {
1336
+ const totalSteps = Math.max(1, parseIntegerOption(steps, 10, 1));
1337
+ let applied = 0;
1338
+
1339
+ for (let i = 1; i <= totalSteps; i++) {
1340
+ const target = Math.round((amount * i) / totalSteps);
1341
+ const delta = target - applied;
1342
+ if (delta !== 0) {
1343
+ robot.scrollMouse(0, delta);
1344
+ applied = target;
1345
+ }
1346
+ await sleep(delay);
1347
+ }
1348
+ }
1349
+
1350
+ function clickCurrentPosition(robot, doubleClick) {
1351
+ if (doubleClick) {
1352
+ robot.mouseClick('left', true);
1353
+ return;
1354
+ }
1355
+ robot.mouseClick();
1356
+ }
1357
+
1358
+ async function clickPoint(robot, point, config, clickOptions = {}) {
1359
+ await smoothMove(robot, point.x, point.y, config.moveSteps, config.moveDelay);
1360
+ clickCurrentPosition(robot, clickOptions.doubleClick === true);
1361
+ }
1362
+
1363
+ async function runConfiguredPath(robot, pathConfig, config) {
1364
+ if (!pathConfig || !Array.isArray(pathConfig.points) || pathConfig.points.length === 0) {
1365
+ return;
1366
+ }
1367
+
1368
+ const clickMode = normalizeClickMode(pathConfig.clickMode, CLICK_MODE.NONE);
1369
+ const interval = toNonNegativeInteger(pathConfig.interval) || 0;
1370
+ const clickDelay = toNonNegativeInteger(pathConfig.clickDelay) || 0;
1371
+ const doubleClick = pathConfig.doubleClick === true;
1372
+
1373
+ for (let index = 0; index < pathConfig.points.length; index++) {
1374
+ const point = pathConfig.points[index];
1375
+ await smoothMove(robot, point.x, point.y, config.moveSteps, config.moveDelay);
1376
+
1377
+ if (shouldClickOnStep(clickMode, index, pathConfig.points.length)) {
1378
+ if (clickDelay > 0) {
1379
+ await sleep(clickDelay);
1380
+ }
1381
+ clickCurrentPosition(robot, doubleClick);
1382
+ }
1383
+
1384
+ if (index < pathConfig.points.length - 1 && interval > 0) {
1385
+ await sleep(interval);
1386
+ }
1387
+ }
1388
+ }
1389
+
1390
+ async function clickTarget(robot, target, config, clickOptions = {}) {
1391
+ if (!target || !target.position) {
1392
+ throw new Error('invalid_target_config');
1393
+ }
1394
+
1395
+ if (target.path) {
1396
+ await runConfiguredPath(robot, target.path, config);
1397
+ }
1398
+
1399
+ await clickPoint(robot, target.position, config, clickOptions);
1400
+ }
1401
+
1402
+ function createActionHandlers(robot, runtime) {
1403
+ const config = runtime.config;
1404
+ let mapStorePromise;
1405
+
1406
+ async function getMapStore() {
1407
+ if (mapStorePromise) {
1408
+ return mapStorePromise;
1409
+ }
1410
+ mapStorePromise = runtime.getMapStore();
1411
+ return mapStorePromise;
1412
+ }
1413
+
1414
+ async function openAppIfNeeded(appName, appConfig, mapStore) {
1415
+ if (getAppState(appConfig) === APP_STATE.OPEN) {
1416
+ return;
1417
+ }
1418
+
1419
+ if (!appConfig.launcher || !appConfig.launcher.position) {
1420
+ throw new Error(`missing_launcher:${appName}`);
1421
+ }
1422
+
1423
+ if (config.launchKey) {
1424
+ robot.keyTap(config.launchKey);
1425
+ }
1426
+
1427
+ if (appConfig.launcher.path) {
1428
+ await clickTarget(robot, appConfig.launcher, config);
1429
+ } else {
1430
+ await smoothMove(robot, 0, 0, config.moveSteps, config.moveDelay);
1431
+ await smoothMove(
1432
+ robot,
1433
+ appConfig.launcher.position.x,
1434
+ appConfig.launcher.position.y,
1435
+ config.moveSteps,
1436
+ config.moveDelay
1437
+ );
1438
+ await smoothMove(
1439
+ robot,
1440
+ appConfig.launcher.position.x + 20,
1441
+ appConfig.launcher.position.y + 10,
1442
+ config.moveSteps,
1443
+ config.moveDelay
1444
+ );
1445
+ await smoothMove(
1446
+ robot,
1447
+ appConfig.launcher.position.x - 20,
1448
+ appConfig.launcher.position.y - 10,
1449
+ config.moveSteps,
1450
+ config.moveDelay
1451
+ );
1452
+ robot.mouseClick();
1453
+ }
1454
+
1455
+ await updateAppState(mapStore, appName, APP_STATE.OPEN);
1456
+ }
1457
+
1458
+ function resolveSequencePoints(command) {
1459
+ const points = normalizePointsValue(
1460
+ command.points !== undefined
1461
+ ? command.points
1462
+ : command.path !== undefined
1463
+ ? command.path
1464
+ : command.command
1465
+ );
1466
+
1467
+ if (!points || points.length < 2) {
1468
+ throw new Error('move_sequence_requires_at_least_2_points');
1469
+ }
1470
+
1471
+ return points;
1472
+ }
1473
+
1474
+ function resolveSequenceInterval(command) {
1475
+ const interval = toNonNegativeInteger(command.interval);
1476
+ if (interval !== null) {
1477
+ return interval;
1478
+ }
1479
+ return config.sequenceInterval;
1480
+ }
1481
+
1482
+ function resolveSequenceClickMode(command) {
1483
+ return normalizeClickMode(command.click, config.sequenceClickMode);
1484
+ }
1485
+
1486
+ function resolveSequenceClickDelay(command) {
1487
+ const clickDelay = toNonNegativeInteger(command.clickDelay);
1488
+ if (clickDelay !== null) {
1489
+ return clickDelay;
1490
+ }
1491
+ return 0;
1492
+ }
1493
+
1494
+ function resolveSequenceDoubleClick(command) {
1495
+ const value = parseBooleanValue(command.doubleClick);
1496
+ if (value !== null) {
1497
+ return value;
1498
+ }
1499
+ return config.sequenceDoubleClick;
1500
+ }
1501
+
1502
+ return {
1503
+ async skip_ads(command) {
1504
+ let point = config.skipAdsPoint;
1505
+
1506
+ if (command.command) {
1507
+ try {
1508
+ const mapStore = await getMapStore();
1509
+ const appRef = resolveAppInMap(mapStore, command.command);
1510
+ const appConfig = appRef.app;
1511
+ if (appConfig) {
1512
+ const button = resolveMappedButton(
1513
+ appConfig,
1514
+ command.button || 'pular_ads'
1515
+ );
1516
+ if (button && button.position) {
1517
+ if (button.path) {
1518
+ await clickTarget(robot, button, config);
1519
+ point = null;
1520
+ } else {
1521
+ point = button.position;
1522
+ }
1523
+ if (button.setState) {
1524
+ await updateAppState(mapStore, appRef.appName, button.setState);
1525
+ }
1526
+ }
1527
+ }
1528
+ } catch (error) {
1529
+ // keep fallback point when map is unavailable for skip_ads
1530
+ }
1531
+ }
1532
+
1533
+ if (point) {
1534
+ await clickPoint(robot, point, config);
1535
+ }
1536
+ },
1537
+
1538
+ async click() {
1539
+ const size = robot.getScreenSize();
1540
+ const centerX = Math.floor(size.width / 2);
1541
+ const centerY = Math.floor(size.height / 2);
1542
+ await clickPoint(robot, { x: centerX, y: centerY }, config);
1543
+ },
1544
+
1545
+ async double_click() {
1546
+ const size = robot.getScreenSize();
1547
+ const centerX = Math.floor(size.width / 2);
1548
+ const centerY = Math.floor(size.height / 2);
1549
+ await clickPoint(
1550
+ robot,
1551
+ { x: centerX, y: centerY },
1552
+ config,
1553
+ { doubleClick: true }
1554
+ );
1555
+ },
1556
+
1557
+ async scroll(command) {
1558
+ const amount = Number(command.command);
1559
+ if (!Number.isFinite(amount)) {
1560
+ throw new Error('scroll_requires_numeric_command');
1561
+ }
1562
+
1563
+ const size = robot.getScreenSize();
1564
+ const centerX = Math.floor(size.width / 2);
1565
+ const centerY = Math.floor(size.height / 2);
1566
+
1567
+ await smoothMove(robot, centerX, centerY, config.moveSteps, config.moveDelay);
1568
+ await smoothScroll(robot, amount, config.scrollSteps, config.scrollDelay);
1569
+ },
1570
+
1571
+ async move_sequence(command) {
1572
+ const points = resolveSequencePoints(command);
1573
+ const interval = resolveSequenceInterval(command);
1574
+ const clickMode = resolveSequenceClickMode(command);
1575
+ const clickDelay = resolveSequenceClickDelay(command);
1576
+ const doubleClick = resolveSequenceDoubleClick(command);
1577
+
1578
+ for (let index = 0; index < points.length; index++) {
1579
+ const point = points[index];
1580
+ await smoothMove(
1581
+ robot,
1582
+ point.x,
1583
+ point.y,
1584
+ config.moveSteps,
1585
+ config.moveDelay
1586
+ );
1587
+
1588
+ if (shouldClickOnStep(clickMode, index, points.length)) {
1589
+ if (clickDelay > 0) {
1590
+ await sleep(clickDelay);
1591
+ }
1592
+ clickCurrentPosition(robot, doubleClick);
1593
+ }
1594
+
1595
+ if (index < points.length - 1 && interval > 0) {
1596
+ await sleep(interval);
1597
+ }
1598
+ }
1599
+ },
1600
+
1601
+ async open_app(command) {
1602
+ const mapStore = await getMapStore();
1603
+ const appRef = resolveAppInMap(mapStore, command.command);
1604
+ if (!appRef.app) {
1605
+ throw new Error(`unknown_app:${appRef.appName}`);
1606
+ }
1607
+
1608
+ await openAppIfNeeded(appRef.appName, appRef.app, mapStore);
1609
+
1610
+ if (command.button) {
1611
+ const mappedButton = resolveMappedButton(appRef.app, command.button);
1612
+ if (!mappedButton) {
1613
+ throw new Error(
1614
+ `unknown_button:${appRef.appName}:${resolveButtonKey(command.button)}`
1615
+ );
1616
+ }
1617
+
1618
+ await clickTarget(robot, mappedButton, config);
1619
+ if (mappedButton.setState) {
1620
+ await updateAppState(mapStore, appRef.appName, mappedButton.setState);
1621
+ }
1622
+ return;
1623
+ }
1624
+
1625
+ if (command.extra) {
1626
+ if (appRef.app.searchIcon && appRef.app.searchIcon.position) {
1627
+ await clickTarget(robot, appRef.app.searchIcon, config);
1628
+ }
1629
+ robot.typeStringDelayed(String(command.extra), config.typeDelay);
1630
+ }
1631
+ },
1632
+
1633
+ async close_app(command) {
1634
+ const mapStore = await getMapStore();
1635
+ const appRef = resolveAppInMap(mapStore, command.command);
1636
+ if (!appRef.app) {
1637
+ throw new Error(`unknown_app:${appRef.appName}`);
1638
+ }
1639
+
1640
+ if (getAppState(appRef.app) === APP_STATE.CLOSED) {
1641
+ return;
1642
+ }
1643
+
1644
+ const mappedButton = resolveMappedButton(
1645
+ appRef.app,
1646
+ command.button || 'fechar'
1647
+ );
1648
+
1649
+ if (mappedButton) {
1650
+ await clickTarget(robot, mappedButton, config);
1651
+ await updateAppState(
1652
+ mapStore,
1653
+ appRef.appName,
1654
+ mappedButton.setState || APP_STATE.CLOSED
1655
+ );
1656
+ return;
1657
+ }
1658
+
1659
+ const modifier =
1660
+ config.launchKey === 'command' ? 'command' : config.launchKey || 'command';
1661
+ robot.keyTap('w', modifier);
1662
+ await updateAppState(mapStore, appRef.appName, APP_STATE.CLOSED);
1663
+ }
1664
+ };
1665
+ }
1666
+
1667
+ async function getRobot() {
1668
+ if (robotCache) {
1669
+ return robotCache;
1670
+ }
1671
+
1672
+ let module;
1673
+ try {
1674
+ module = await import('robotjs');
1675
+ } catch (error) {
1676
+ throw new Error(
1677
+ 'robotjs_not_available: install dependencies and run in a supported desktop environment.'
1678
+ );
1679
+ }
1680
+
1681
+ const robot = module && module.default ? module.default : module;
1682
+ if (!robot || typeof robot.mouseClick !== 'function') {
1683
+ throw new Error(
1684
+ 'robotjs_not_available: install dependencies and run in a supported desktop environment.'
1685
+ );
1686
+ }
1687
+
1688
+ robotCache = robot;
1689
+ return robot;
1690
+ }