command-cmd 1.0.2 → 1.0.5

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 +2165 -16
  2. package/doc.md +292 -152
  3. package/docPTBR.md +299 -152
  4. package/map.md +244 -0
  5. package/mapPTBR.md +244 -0
  6. package/package.json +5 -2
package/cmd.js CHANGED
@@ -1,3 +1,135 @@
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+ import { fileURLToPath } from 'url';
4
+
5
+ const { readFile, writeFile, mkdir } = fs.promises;
6
+ const { resolve: resolvePath, dirname: dirnamePath } = path;
7
+
8
+ const MODULE_DIR = dirnamePath(fileURLToPath(import.meta.url));
9
+
10
+ const DEFAULT_APPS = Object.freeze({
11
+ spotify: {
12
+ launcher: { x: -2, y: 145 },
13
+ searchIcon: { x: 584, y: 90 }
14
+ },
15
+ chrome: {
16
+ launcher: { x: 20, y: 219 },
17
+ searchIcon: { x: 546, y: 87 }
18
+ },
19
+ youtube: {
20
+ launcher: { x: 20, y: 225 },
21
+ searchIcon: { x: 575, y: 144 }
22
+ }
23
+ });
24
+
25
+ const DOC_TEMPLATE_MAP = Object.freeze({
26
+ doc_en: Object.freeze({
27
+ key: 'doc_en',
28
+ source: 'doc.md',
29
+ target: 'doc.md',
30
+ isMapDoc: false
31
+ }),
32
+ doc_ptbr: Object.freeze({
33
+ key: 'doc_ptbr',
34
+ source: 'docPTBR.md',
35
+ target: 'docPTBR.md',
36
+ isMapDoc: false
37
+ }),
38
+ map_en: Object.freeze({
39
+ key: 'map_en',
40
+ source: 'map.md',
41
+ target: 'map.md',
42
+ isMapDoc: true
43
+ }),
44
+ map_ptbr: Object.freeze({
45
+ key: 'map_ptbr',
46
+ source: 'mapPTBR.md',
47
+ target: 'mapPTBR.md',
48
+ isMapDoc: true
49
+ })
50
+ });
51
+
52
+ const DOC_TEMPLATES = Object.freeze(Object.values(DOC_TEMPLATE_MAP));
53
+
54
+ const ACTION_ALIASES = Object.freeze({
55
+ skip_ads: 'skip_ads',
56
+ skipads: 'skip_ads',
57
+ pular_ads: 'skip_ads',
58
+ pularads: 'skip_ads',
59
+ click: 'click',
60
+ clicar: 'click',
61
+ double_click: 'double_click',
62
+ doubleclick: 'double_click',
63
+ clique_duplo: 'double_click',
64
+ cliqueduplo: 'double_click',
65
+ scroll: 'scroll',
66
+ rolar: 'scroll',
67
+ move_sequence: 'move_sequence',
68
+ movesequence: 'move_sequence',
69
+ mover_sequencia: 'move_sequence',
70
+ moversequencia: 'move_sequence',
71
+ sequencia_movimento: 'move_sequence',
72
+ sequence_move: 'move_sequence',
73
+ open_app: 'open_app',
74
+ openapp: 'open_app',
75
+ abrir_app: 'open_app',
76
+ abrirapp: 'open_app',
77
+ close_app: 'close_app',
78
+ closeapp: 'close_app',
79
+ fechar_app: 'close_app',
80
+ fecharapp: 'close_app',
81
+ key_tap: 'key_tap',
82
+ keytap: 'key_tap',
83
+ tap_key: 'key_tap',
84
+ tapkey: 'key_tap',
85
+ press_key: 'key_tap',
86
+ pressionar_tecla: 'key_tap',
87
+ tecla: 'key_tap',
88
+ type_string_delayed: 'type_string_delayed',
89
+ typestringdelayed: 'type_string_delayed',
90
+ type_string: 'type_string_delayed',
91
+ typestring: 'type_string_delayed',
92
+ digitar: 'type_string_delayed',
93
+ escrever: 'type_string_delayed'
94
+ });
95
+
96
+ const BUTTON_ALIASES = Object.freeze({
97
+ pularads: 'pular_ads',
98
+ pular_ads: 'pular_ads',
99
+ pular_anuncio: 'pular_ads',
100
+ pular_anuncios: 'pular_ads',
101
+ skip_ads: 'pular_ads',
102
+ skipad: 'pular_ads',
103
+ search: 'barra_pesquisa',
104
+ searchbar: 'barra_pesquisa',
105
+ search_bar: 'barra_pesquisa',
106
+ barra_pesquisa: 'barra_pesquisa',
107
+ barrapesquisa: 'barra_pesquisa',
108
+ fechar: 'fechar',
109
+ close: 'fechar',
110
+ close_app: 'fechar'
111
+ });
112
+
113
+ const APP_ALIASES = Object.freeze({
114
+ google: 'chrome',
115
+ google_chrome: 'chrome'
116
+ });
117
+
118
+ const APP_STATE = Object.freeze({
119
+ OPEN: 'aberto',
120
+ CLOSED: 'fechado'
121
+ });
122
+
123
+ const CLICK_MODE = Object.freeze({
124
+ NONE: 'none',
125
+ BETWEEN: 'between',
126
+ EACH: 'each',
127
+ FIRST: 'first',
128
+ LAST: 'last'
129
+ });
130
+
131
+ let robotCache;
132
+
1
133
  export function cmd(message) {
2
134
  if (typeof message !== 'string') {
3
135
  return [];
@@ -11,10 +143,21 @@ export function cmd(message) {
11
143
  const inside = match[1].trim();
12
144
  if (!inside) continue;
13
145
 
14
- const obj = {
146
+ const command = {
15
147
  type: undefined,
16
148
  command: undefined,
17
149
  extra: undefined,
150
+ button: undefined,
151
+ key: undefined,
152
+ modifiers: undefined,
153
+ text: undefined,
154
+ typeDelay: undefined,
155
+ points: undefined,
156
+ path: undefined,
157
+ interval: undefined,
158
+ clickDelay: undefined,
159
+ click: undefined,
160
+ doubleClick: undefined,
18
161
  seq: undefined
19
162
  };
20
163
 
@@ -24,6 +167,13 @@ export function cmd(message) {
24
167
  const part = rawPart.trim();
25
168
  if (!part) continue;
26
169
 
170
+ if (part.indexOf(':') === -1) {
171
+ if (!command.type) {
172
+ command.type = part;
173
+ }
174
+ continue;
175
+ }
176
+
27
177
  const [rawKey, ...rest] = part.split(':');
28
178
  if (!rawKey || rest.length === 0) continue;
29
179
 
@@ -32,28 +182,111 @@ export function cmd(message) {
32
182
  if (!value) continue;
33
183
 
34
184
  if (key === 'type' || key === 'tipo') {
35
- obj.type = value;
185
+ command.type = value;
36
186
  } else if (key === 'command' || key === 'comando') {
37
- obj.command = value;
187
+ command.command = value;
38
188
  } else if (key === 'extra') {
39
- obj.extra = value;
189
+ command.extra = value;
190
+ } else if (key === 'button' || key === 'botao' || key === 'botão') {
191
+ command.button = value;
192
+ } else if (key === 'key' || key === 'tecla' || key === 'hotkey') {
193
+ command.key = value;
194
+ if (!command.command) {
195
+ command.command = value;
196
+ }
197
+ } else if (
198
+ key === 'modifier' ||
199
+ key === 'modifiers' ||
200
+ key === 'modificador' ||
201
+ key === 'modificadores'
202
+ ) {
203
+ command.modifiers = value;
204
+ } else if (
205
+ key === 'text' ||
206
+ key === 'texto' ||
207
+ key === 'string' ||
208
+ key === 'mensagem'
209
+ ) {
210
+ command.text = value;
211
+ if (!command.command) {
212
+ command.command = value;
213
+ }
214
+ } else if (
215
+ key === 'typedelay' ||
216
+ key === 'type_delay' ||
217
+ key === 'typingdelay' ||
218
+ key === 'typing_delay' ||
219
+ key === 'textdelay' ||
220
+ key === 'delaytexto' ||
221
+ key === 'delaydigitacao' ||
222
+ key === 'delaydigitar'
223
+ ) {
224
+ const typeDelay = toNonNegativeInteger(value);
225
+ if (typeDelay !== null) {
226
+ command.typeDelay = typeDelay;
227
+ }
228
+ } else if (
229
+ key === 'points' ||
230
+ key === 'pontos' ||
231
+ key === 'path' ||
232
+ key === 'trajeto' ||
233
+ key === 'caminho' ||
234
+ key === 'coords' ||
235
+ key === 'coordenadas'
236
+ ) {
237
+ command.points = value;
238
+ command.path = value;
239
+ if (!command.command) {
240
+ command.command = value;
241
+ }
242
+ } else if (key === 'interval' || key === 'intervalo') {
243
+ const interval = toNonNegativeInteger(value);
244
+ if (interval !== null) {
245
+ command.interval = interval;
246
+ }
247
+ } else if (
248
+ key === 'clickdelay' ||
249
+ key === 'delayclick' ||
250
+ key === 'delay_clique' ||
251
+ key === 'delayclique' ||
252
+ key === 'click_delay' ||
253
+ key === 'delay'
254
+ ) {
255
+ const clickDelay = toNonNegativeInteger(value);
256
+ if (clickDelay !== null) {
257
+ command.clickDelay = clickDelay;
258
+ }
259
+ } else if (key === 'click' || key === 'clique') {
260
+ command.click = value;
261
+ } else if (
262
+ key === 'doubleclick' ||
263
+ key === 'double_click' ||
264
+ key === 'double' ||
265
+ key === 'duplo' ||
266
+ key === 'cliqueduplo' ||
267
+ key === 'clique_duplo'
268
+ ) {
269
+ const doubleClick = parseBooleanValue(value);
270
+ if (doubleClick !== null) {
271
+ command.doubleClick = doubleClick;
272
+ }
40
273
  } else if (key === 'seq') {
41
- const seq = Number(value);
42
- if (Number.isInteger(seq) && seq >= 0) {
43
- obj.seq = seq;
274
+ const seq = toNonNegativeInteger(value);
275
+ if (seq !== null) {
276
+ command.seq = seq;
44
277
  }
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;
278
+ } else if (!command.type) {
279
+ command.type = rawKey.trim();
280
+ command.command = value;
281
+ } else if (!command.command) {
282
+ command.command = value;
283
+ } else if (!command.extra) {
284
+ command.extra = value;
52
285
  }
53
286
  }
54
287
 
55
- if (obj.type && obj.command) {
56
- results.push(obj);
288
+ if (hasCommandPayload(command)) {
289
+ results.push(command);
57
290
  }
58
291
  }
59
292
 
@@ -64,3 +297,1919 @@ export function extractFirstCommand(message) {
64
297
  const [first] = cmd(message);
65
298
  return first || null;
66
299
  }
300
+
301
+ export async function initMap(options = {}) {
302
+ const config = buildCursorConfig(options);
303
+ const mapAlreadyExists = await pathExists(config.mapPath);
304
+ const store = await createMapStore(config);
305
+
306
+ const persistMerged =
307
+ parseBooleanValue(options && options.persistMerged) === true ||
308
+ parseBooleanValue(options && options.writeMerged) === true;
309
+
310
+ if (!mapAlreadyExists && config.persistMap) {
311
+ // already persisted by createMapStore when file is missing
312
+ } else if (persistMerged && config.persistMap) {
313
+ await persistMapStore(store);
314
+ }
315
+
316
+ return {
317
+ path: store.path,
318
+ created: !mapAlreadyExists,
319
+ persisted: (!mapAlreadyExists || persistMerged) && config.persistMap,
320
+ version: store.map.version,
321
+ appCount: Object.keys(store.map.apps || {}).length,
322
+ map: store.map
323
+ };
324
+ }
325
+
326
+ export async function initDoc(options = {}) {
327
+ const raw = options && typeof options === 'object' ? options : {};
328
+ const outputDir = resolveDocOutputDir(raw);
329
+ const overwrite = parseBooleanValue(raw.overwrite) === true;
330
+ const includeMapDocs = shouldIncludeMapDocs(raw.includeMapDocs);
331
+ const templates = resolveDocTemplates(raw.files, includeMapDocs);
332
+
333
+ await mkdir(outputDir, { recursive: true });
334
+
335
+ const created = [];
336
+ const updated = [];
337
+ const skipped = [];
338
+
339
+ for (const template of templates) {
340
+ const sourcePath = resolvePath(MODULE_DIR, template.source);
341
+ const targetPath = resolvePath(outputDir, template.target);
342
+ const targetExists = await pathExists(targetPath);
343
+
344
+ if (targetExists && !overwrite) {
345
+ skipped.push({
346
+ key: template.key,
347
+ path: targetPath,
348
+ reason: 'exists'
349
+ });
350
+ continue;
351
+ }
352
+
353
+ let content;
354
+ try {
355
+ content = await readFile(sourcePath, 'utf8');
356
+ } catch (error) {
357
+ if (error && error.code === 'ENOENT') {
358
+ throw new Error(`doc_template_not_found:${template.source}`);
359
+ }
360
+ throw error;
361
+ }
362
+
363
+ await writeFile(targetPath, content, 'utf8');
364
+
365
+ if (targetExists) {
366
+ updated.push({ key: template.key, path: targetPath });
367
+ } else {
368
+ created.push({ key: template.key, path: targetPath });
369
+ }
370
+ }
371
+
372
+ return {
373
+ outputDir,
374
+ overwrite,
375
+ created,
376
+ updated,
377
+ skipped
378
+ };
379
+ }
380
+
381
+ export async function cursor(input, options = {}) {
382
+ const commands = normalizeCursorInput(input);
383
+ if (commands.length === 0) {
384
+ return {
385
+ executed: [],
386
+ skipped: [{ reason: 'no_commands' }],
387
+ totalCommands: 0,
388
+ totalExecutions: 0
389
+ };
390
+ }
391
+
392
+ const runtime = createCursorRuntime(options);
393
+ let actions;
394
+ const executed = [];
395
+ const skipped = [];
396
+
397
+ async function getActions() {
398
+ if (actions) {
399
+ return actions;
400
+ }
401
+ const robot = await getRobot();
402
+ actions = createActionHandlers(robot, runtime);
403
+ return actions;
404
+ }
405
+
406
+ for (let commandIndex = 0; commandIndex < commands.length; commandIndex++) {
407
+ const command = commands[commandIndex];
408
+ const actionKey = resolveActionKey(command.type);
409
+
410
+ if (!actionKey) {
411
+ skipped.push({
412
+ commandIndex,
413
+ type: command.type,
414
+ command: command.command,
415
+ reason: 'unknown_type'
416
+ });
417
+ continue;
418
+ }
419
+
420
+ const repeatCount = getRepeatCount(command.seq);
421
+ if (repeatCount === 0) {
422
+ skipped.push({
423
+ commandIndex,
424
+ type: command.type,
425
+ command: command.command,
426
+ reason: 'seq_zero'
427
+ });
428
+ continue;
429
+ }
430
+
431
+ if (isMapAwareAction(actionKey)) {
432
+ try {
433
+ await runtime.getMapStore();
434
+ } catch (error) {
435
+ skipped.push({
436
+ commandIndex,
437
+ type: command.type,
438
+ command: command.command,
439
+ reason: 'map_unavailable',
440
+ detail: error instanceof Error ? error.message : String(error)
441
+ });
442
+ continue;
443
+ }
444
+ }
445
+
446
+ let action;
447
+ try {
448
+ const actionHandlers = await getActions();
449
+ action = actionHandlers[actionKey];
450
+ } catch (error) {
451
+ skipped.push({
452
+ commandIndex,
453
+ type: command.type,
454
+ command: command.command,
455
+ reason: 'robot_unavailable',
456
+ detail: error instanceof Error ? error.message : String(error)
457
+ });
458
+ continue;
459
+ }
460
+
461
+ if (!action) {
462
+ skipped.push({
463
+ commandIndex,
464
+ type: command.type,
465
+ command: command.command,
466
+ reason: 'unknown_type'
467
+ });
468
+ continue;
469
+ }
470
+
471
+ for (let iteration = 1; iteration <= repeatCount; iteration++) {
472
+ try {
473
+ await action(command);
474
+ executed.push({
475
+ commandIndex,
476
+ iteration,
477
+ type: command.type,
478
+ command: command.command,
479
+ extra: command.extra,
480
+ button: command.button,
481
+ key: command.key,
482
+ modifiers: command.modifiers,
483
+ text: command.text,
484
+ typeDelay: command.typeDelay,
485
+ points: command.points,
486
+ path: command.path,
487
+ interval: command.interval,
488
+ clickDelay: command.clickDelay,
489
+ click: command.click,
490
+ doubleClick: command.doubleClick,
491
+ seq: command.seq
492
+ });
493
+ } catch (error) {
494
+ skipped.push({
495
+ commandIndex,
496
+ iteration,
497
+ type: command.type,
498
+ command: command.command,
499
+ reason: 'action_error',
500
+ detail: error instanceof Error ? error.message : String(error)
501
+ });
502
+ break;
503
+ }
504
+ }
505
+ }
506
+
507
+ return {
508
+ executed,
509
+ skipped,
510
+ totalCommands: commands.length,
511
+ totalExecutions: executed.length
512
+ };
513
+ }
514
+
515
+ function createCursorRuntime(options) {
516
+ const config = buildCursorConfig(options);
517
+ let mapStorePromise;
518
+
519
+ return {
520
+ config,
521
+ async getMapStore() {
522
+ if (mapStorePromise) {
523
+ return mapStorePromise;
524
+ }
525
+ mapStorePromise = createMapStore(config);
526
+ return mapStorePromise;
527
+ }
528
+ };
529
+ }
530
+
531
+ function buildCursorConfig(options) {
532
+ const raw = options && typeof options === 'object' ? options : {};
533
+ const mapPath =
534
+ typeof raw.mapPath === 'string' && raw.mapPath.trim()
535
+ ? raw.mapPath.trim()
536
+ : 'map.json';
537
+
538
+ return {
539
+ mapPath: resolvePath(mapPath),
540
+ persistMap: raw.persistMap !== false,
541
+ createMapIfMissing: raw.createMapIfMissing !== false,
542
+ apps: mergeOptionApps(raw.apps),
543
+ launchKey:
544
+ raw.launchKey === false || raw.launchKey === null
545
+ ? undefined
546
+ : typeof raw.launchKey === 'string' && raw.launchKey.trim()
547
+ ? raw.launchKey.trim()
548
+ : 'command',
549
+ typeDelay: parseIntegerOption(raw.typeDelay, 50, 0),
550
+ moveSteps: parseIntegerOption(raw.moveSteps, 50, 1),
551
+ moveDelay: parseIntegerOption(raw.moveDelay, 8, 0),
552
+ scrollSteps: parseIntegerOption(raw.scrollSteps, 10, 1),
553
+ scrollDelay: parseIntegerOption(raw.scrollDelay, 20, 0),
554
+ sequenceInterval: parseIntegerOption(raw.sequenceInterval, 250, 0),
555
+ sequenceClickMode: normalizeClickMode(raw.sequenceClick, CLICK_MODE.NONE),
556
+ sequenceDoubleClick:
557
+ parseBooleanValue(raw.sequenceDoubleClick) === true,
558
+ defaultClosePoint: parsePoint(raw.defaultClosePoint) || { x: 1888, y: 16 },
559
+ skipAdsPoint: parsePoint(raw.skipAdsPoint) || { x: 808, y: 569 }
560
+ };
561
+ }
562
+
563
+ function mergeOptionApps(customApps) {
564
+ const merged = {};
565
+
566
+ for (const [appName, appConfig] of Object.entries(DEFAULT_APPS)) {
567
+ merged[appName] = cloneAppMap(appConfig);
568
+ }
569
+
570
+ if (!customApps || typeof customApps !== 'object') {
571
+ return merged;
572
+ }
573
+
574
+ for (const [rawName, rawConfig] of Object.entries(customApps)) {
575
+ const appName = resolveAppName(rawName);
576
+ const normalized = normalizeAppConfig(rawConfig);
577
+ if (!normalized) continue;
578
+
579
+ const current = merged[appName] || {
580
+ launcher: undefined,
581
+ searchIcon: undefined,
582
+ state: APP_STATE.CLOSED,
583
+ buttons: {}
584
+ };
585
+
586
+ merged[appName] = {
587
+ launcher: normalized.launcher || current.launcher,
588
+ searchIcon: normalized.searchIcon || current.searchIcon,
589
+ state: normalized.state || current.state || APP_STATE.CLOSED,
590
+ buttons: {
591
+ ...(current.buttons || {}),
592
+ ...(normalized.buttons || {})
593
+ }
594
+ };
595
+ }
596
+
597
+ return merged;
598
+ }
599
+
600
+ function toCommandText(value) {
601
+ if (typeof value === 'string') {
602
+ const trimmed = value.trim();
603
+ return trimmed || '';
604
+ }
605
+
606
+ if (Number.isFinite(value)) {
607
+ return String(value);
608
+ }
609
+
610
+ return '';
611
+ }
612
+
613
+ function hasCommandPayload(command) {
614
+ if (!command || !command.type) {
615
+ return false;
616
+ }
617
+
618
+ const actionKey = resolveActionKey(command.type);
619
+
620
+ if (actionKey === 'key_tap') {
621
+ return Boolean(toCommandText(command.key) || toCommandText(command.command));
622
+ }
623
+
624
+ if (actionKey === 'type_string_delayed') {
625
+ return Boolean(
626
+ toCommandText(command.text) ||
627
+ toCommandText(command.command) ||
628
+ toCommandText(command.extra)
629
+ );
630
+ }
631
+
632
+ if (actionKey === 'move_sequence') {
633
+ return Boolean(command.points || command.path || toCommandText(command.command));
634
+ }
635
+
636
+ return Boolean(toCommandText(command.command));
637
+ }
638
+
639
+ function normalizeCommandObject(value) {
640
+ if (!value || typeof value !== 'object') {
641
+ return null;
642
+ }
643
+
644
+ const rawType = value.type !== undefined ? value.type : value.tipo;
645
+ const rawCommand = value.command !== undefined ? value.command : value.comando;
646
+ const rawKey =
647
+ value.key !== undefined
648
+ ? value.key
649
+ : value.tecla !== undefined
650
+ ? value.tecla
651
+ : value.hotkey;
652
+ const rawModifiers =
653
+ value.modifiers !== undefined
654
+ ? value.modifiers
655
+ : value.modifier !== undefined
656
+ ? value.modifier
657
+ : value.modificadores !== undefined
658
+ ? value.modificadores
659
+ : value.modificador;
660
+ const rawText =
661
+ value.text !== undefined
662
+ ? value.text
663
+ : value.texto !== undefined
664
+ ? value.texto
665
+ : value.string !== undefined
666
+ ? value.string
667
+ : value.mensagem;
668
+ const rawButton =
669
+ value.button !== undefined
670
+ ? value.button
671
+ : value.botao !== undefined
672
+ ? value.botao
673
+ : value['botão'];
674
+ const rawPoints =
675
+ value.points !== undefined
676
+ ? value.points
677
+ : value.pontos !== undefined
678
+ ? value.pontos
679
+ : value.path !== undefined
680
+ ? value.path
681
+ : value.trajeto !== undefined
682
+ ? value.trajeto
683
+ : value.caminho !== undefined
684
+ ? value.caminho
685
+ : value.coords !== undefined
686
+ ? value.coords
687
+ : value.coordenadas;
688
+ const rawPath =
689
+ value.path !== undefined
690
+ ? value.path
691
+ : value.caminho !== undefined
692
+ ? value.caminho
693
+ : value.trajeto;
694
+ const rawInterval =
695
+ value.interval !== undefined ? value.interval : value.intervalo;
696
+ const rawClickDelay =
697
+ value.clickDelay !== undefined
698
+ ? value.clickDelay
699
+ : value.delayClick !== undefined
700
+ ? value.delayClick
701
+ : value.delayClique !== undefined
702
+ ? value.delayClique
703
+ : value.delay;
704
+ const rawClick = value.click !== undefined ? value.click : value.clique;
705
+ const rawDoubleClick =
706
+ value.doubleClick !== undefined
707
+ ? value.doubleClick
708
+ : value.double_click !== undefined
709
+ ? value.double_click
710
+ : value.duplo !== undefined
711
+ ? value.duplo
712
+ : value.cliqueDuplo !== undefined
713
+ ? value.cliqueDuplo
714
+ : value.clique_duplo;
715
+ const rawTypeDelay =
716
+ value.typeDelay !== undefined
717
+ ? value.typeDelay
718
+ : value.type_delay !== undefined
719
+ ? value.type_delay
720
+ : value.typingDelay !== undefined
721
+ ? value.typingDelay
722
+ : value.typing_delay !== undefined
723
+ ? value.typing_delay
724
+ : value.textDelay !== undefined
725
+ ? value.textDelay
726
+ : value.delayTexto !== undefined
727
+ ? value.delayTexto
728
+ : value.delayDigitacao !== undefined
729
+ ? value.delayDigitacao
730
+ : value.delayDigitar;
731
+
732
+ const type =
733
+ typeof rawType === 'string' ? rawType.trim() : String(rawType || '').trim();
734
+ const actionKey = resolveActionKey(type);
735
+
736
+ const normalizedPoints = normalizePointsValue(
737
+ rawPoints !== undefined ? rawPoints : rawPath
738
+ );
739
+
740
+ const key = toCommandText(rawKey);
741
+ const text = toCommandText(rawText);
742
+
743
+ let command = toCommandText(rawCommand);
744
+
745
+ if (!command && normalizedPoints) {
746
+ command = serializePoints(normalizedPoints);
747
+ }
748
+
749
+ if (!command && text) {
750
+ command = text;
751
+ }
752
+
753
+ if (!command && key) {
754
+ command = key;
755
+ }
756
+
757
+ const extra =
758
+ value.extra === undefined || value.extra === null
759
+ ? undefined
760
+ : toCommandText(value.extra) || String(value.extra);
761
+
762
+ if (!command && actionKey === 'type_string_delayed' && extra) {
763
+ command = extra;
764
+ }
765
+
766
+ const normalized = {
767
+ type,
768
+ command,
769
+ extra,
770
+ button: undefined,
771
+ key: undefined,
772
+ modifiers: undefined,
773
+ text: undefined,
774
+ typeDelay: undefined,
775
+ points: undefined,
776
+ path: undefined,
777
+ interval: undefined,
778
+ clickDelay: undefined,
779
+ click: undefined,
780
+ doubleClick: undefined,
781
+ seq: undefined
782
+ };
783
+
784
+ if (rawButton !== undefined && rawButton !== null) {
785
+ const buttonText = String(rawButton).trim();
786
+ if (buttonText) {
787
+ normalized.button = buttonText;
788
+ }
789
+ }
790
+
791
+ if (key) {
792
+ normalized.key = key;
793
+ }
794
+
795
+ const modifiers = parseKeyModifiers(rawModifiers);
796
+ if (modifiers && modifiers.length > 0) {
797
+ normalized.modifiers = modifiers;
798
+ }
799
+
800
+ if (text) {
801
+ normalized.text = text;
802
+ }
803
+
804
+ const typeDelay = toNonNegativeInteger(rawTypeDelay);
805
+ if (typeDelay !== null) {
806
+ normalized.typeDelay = typeDelay;
807
+ }
808
+
809
+ if (normalizedPoints) {
810
+ normalized.points = normalizedPoints;
811
+ normalized.path = normalizedPoints;
812
+ }
813
+
814
+ const interval = toNonNegativeInteger(rawInterval);
815
+ if (interval !== null) {
816
+ normalized.interval = interval;
817
+ }
818
+
819
+ const clickDelay = toNonNegativeInteger(rawClickDelay);
820
+ if (clickDelay !== null) {
821
+ normalized.clickDelay = clickDelay;
822
+ }
823
+
824
+ if (rawClick !== undefined && rawClick !== null) {
825
+ const clickMode = String(rawClick).trim();
826
+ if (clickMode) {
827
+ normalized.click = clickMode;
828
+ }
829
+ }
830
+
831
+ const doubleClick = parseBooleanValue(rawDoubleClick);
832
+ if (doubleClick !== null) {
833
+ normalized.doubleClick = doubleClick;
834
+ }
835
+
836
+ const rawSeq = value.seq !== undefined ? value.seq : value.sequencia;
837
+ const seq = toNonNegativeInteger(rawSeq);
838
+ if (seq !== null) {
839
+ normalized.seq = seq;
840
+ }
841
+
842
+ return hasCommandPayload(normalized) ? normalized : null;
843
+ }
844
+
845
+ function normalizeCursorInput(input) {
846
+ if (typeof input === 'string') {
847
+ return cmd(input);
848
+ }
849
+
850
+ if (Array.isArray(input)) {
851
+ return input.map(normalizeCommandObject).filter(Boolean);
852
+ }
853
+
854
+ const single = normalizeCommandObject(input);
855
+ return single ? [single] : [];
856
+ }
857
+
858
+ function getRepeatCount(seq) {
859
+ const parsed = toNonNegativeInteger(seq);
860
+ if (parsed === null) {
861
+ return 1;
862
+ }
863
+ return parsed;
864
+ }
865
+
866
+ function toNonNegativeInteger(value) {
867
+ const parsed = Number(value);
868
+ if (!Number.isInteger(parsed) || parsed < 0) {
869
+ return null;
870
+ }
871
+ return parsed;
872
+ }
873
+
874
+ function parseIntegerOption(value, fallback, min) {
875
+ const parsed = Number(value);
876
+ if (!Number.isInteger(parsed) || parsed < min) {
877
+ return fallback;
878
+ }
879
+ return parsed;
880
+ }
881
+
882
+ function parseBooleanValue(value) {
883
+ if (value === undefined || value === null) {
884
+ return null;
885
+ }
886
+
887
+ if (typeof value === 'boolean') {
888
+ return value;
889
+ }
890
+
891
+ const token = canonicalToken(value);
892
+ if (
893
+ token === 'true' ||
894
+ token === '1' ||
895
+ token === 'sim' ||
896
+ token === 'yes' ||
897
+ token === 'on'
898
+ ) {
899
+ return true;
900
+ }
901
+
902
+ if (
903
+ token === 'false' ||
904
+ token === '0' ||
905
+ token === 'nao' ||
906
+ token === 'no' ||
907
+ token === 'off'
908
+ ) {
909
+ return false;
910
+ }
911
+
912
+ return null;
913
+ }
914
+
915
+ function normalizeClickMode(value, fallback) {
916
+ if (value === true) {
917
+ return CLICK_MODE.BETWEEN;
918
+ }
919
+
920
+ if (value === false) {
921
+ return CLICK_MODE.NONE;
922
+ }
923
+
924
+ const token = canonicalToken(value);
925
+
926
+ if (
927
+ token === CLICK_MODE.EACH ||
928
+ token === 'cada' ||
929
+ token === 'todos' ||
930
+ token === 'todas'
931
+ ) {
932
+ return CLICK_MODE.EACH;
933
+ }
934
+
935
+ if (token === CLICK_MODE.BETWEEN || token === 'entre') {
936
+ return CLICK_MODE.BETWEEN;
937
+ }
938
+
939
+ if (token === CLICK_MODE.FIRST || token === 'primeiro') {
940
+ return CLICK_MODE.FIRST;
941
+ }
942
+
943
+ if (token === CLICK_MODE.LAST || token === 'ultimo' || token === 'final') {
944
+ return CLICK_MODE.LAST;
945
+ }
946
+
947
+ if (
948
+ token === CLICK_MODE.NONE ||
949
+ token === 'sem' ||
950
+ token === 'nenhum' ||
951
+ token === 'desligado'
952
+ ) {
953
+ return CLICK_MODE.NONE;
954
+ }
955
+
956
+ return fallback || CLICK_MODE.NONE;
957
+ }
958
+
959
+ function parsePointToken(token) {
960
+ const value = String(token || '').trim();
961
+ if (!value) return null;
962
+
963
+ const match = value.match(/^(-?\d+)\s*(?:x|:|,)\s*(-?\d+)$/i);
964
+ if (!match) return null;
965
+
966
+ return {
967
+ x: Number(match[1]),
968
+ y: Number(match[2])
969
+ };
970
+ }
971
+
972
+ function normalizePointsValue(value) {
973
+ if (value === undefined || value === null) {
974
+ return undefined;
975
+ }
976
+
977
+ if (Array.isArray(value)) {
978
+ const points = [];
979
+ for (const item of value) {
980
+ const point = parsePoint(item) || parsePointToken(item);
981
+ if (!point) {
982
+ return undefined;
983
+ }
984
+ points.push(point);
985
+ }
986
+ return points.length > 0 ? points : undefined;
987
+ }
988
+
989
+ if (typeof value === 'string') {
990
+ const parts = value
991
+ .split(/[|;>]/g)
992
+ .map((part) => part.trim())
993
+ .filter(Boolean);
994
+
995
+ if (parts.length === 0) {
996
+ return undefined;
997
+ }
998
+
999
+ const points = [];
1000
+ for (const part of parts) {
1001
+ const point = parsePointToken(part);
1002
+ if (!point) {
1003
+ return undefined;
1004
+ }
1005
+ points.push(point);
1006
+ }
1007
+ return points.length > 0 ? points : undefined;
1008
+ }
1009
+
1010
+ const singlePoint = parsePoint(value);
1011
+ if (singlePoint) {
1012
+ return [singlePoint];
1013
+ }
1014
+
1015
+ return undefined;
1016
+ }
1017
+
1018
+ function serializePoints(points) {
1019
+ if (!Array.isArray(points) || points.length === 0) {
1020
+ return '';
1021
+ }
1022
+
1023
+ return points.map((point) => `${point.x}x${point.y}`).join('|');
1024
+ }
1025
+
1026
+ function normalizePathConfig(rawPath) {
1027
+ if (rawPath === undefined || rawPath === null) {
1028
+ return undefined;
1029
+ }
1030
+
1031
+ let points;
1032
+ let clickMode = CLICK_MODE.NONE;
1033
+ let interval = 0;
1034
+ let clickDelay = 0;
1035
+ let doubleClick = false;
1036
+
1037
+ if (typeof rawPath === 'string' || Array.isArray(rawPath)) {
1038
+ points = normalizePointsValue(rawPath);
1039
+ } else if (typeof rawPath === 'object') {
1040
+ points = normalizePointsValue(
1041
+ rawPath.points !== undefined
1042
+ ? rawPath.points
1043
+ : rawPath.pontos !== undefined
1044
+ ? rawPath.pontos
1045
+ : rawPath.path !== undefined
1046
+ ? rawPath.path
1047
+ : rawPath.caminho !== undefined
1048
+ ? rawPath.caminho
1049
+ : rawPath.coords !== undefined
1050
+ ? rawPath.coords
1051
+ : rawPath.coordenadas
1052
+ );
1053
+
1054
+ clickMode = normalizeClickMode(
1055
+ rawPath.click !== undefined ? rawPath.click : rawPath.clique,
1056
+ CLICK_MODE.NONE
1057
+ );
1058
+
1059
+ interval =
1060
+ toNonNegativeInteger(
1061
+ rawPath.interval !== undefined ? rawPath.interval : rawPath.intervalo
1062
+ ) || 0;
1063
+
1064
+ clickDelay =
1065
+ toNonNegativeInteger(
1066
+ rawPath.clickDelay !== undefined
1067
+ ? rawPath.clickDelay
1068
+ : rawPath.delayClick !== undefined
1069
+ ? rawPath.delayClick
1070
+ : rawPath.delayClique !== undefined
1071
+ ? rawPath.delayClique
1072
+ : rawPath.delay
1073
+ ) || 0;
1074
+
1075
+ doubleClick =
1076
+ parseBooleanValue(
1077
+ rawPath.doubleClick !== undefined
1078
+ ? rawPath.doubleClick
1079
+ : rawPath.double_click !== undefined
1080
+ ? rawPath.double_click
1081
+ : rawPath.duplo
1082
+ ) === true;
1083
+ } else {
1084
+ return undefined;
1085
+ }
1086
+
1087
+ if (!points || points.length === 0) {
1088
+ return undefined;
1089
+ }
1090
+
1091
+ return {
1092
+ points,
1093
+ clickMode,
1094
+ interval,
1095
+ clickDelay,
1096
+ doubleClick
1097
+ };
1098
+ }
1099
+
1100
+ function normalizeTargetConfig(rawTarget) {
1101
+ if (rawTarget === undefined || rawTarget === null) {
1102
+ return undefined;
1103
+ }
1104
+
1105
+ if (typeof rawTarget !== 'object') {
1106
+ const directPoint = parsePoint(rawTarget);
1107
+ return directPoint ? { position: directPoint } : undefined;
1108
+ }
1109
+
1110
+ let position =
1111
+ parsePoint(
1112
+ rawTarget.position !== undefined
1113
+ ? rawTarget.position
1114
+ : rawTarget.posicao !== undefined
1115
+ ? rawTarget.posicao
1116
+ : rawTarget.destino !== undefined
1117
+ ? rawTarget.destino
1118
+ : rawTarget.target !== undefined
1119
+ ? rawTarget.target
1120
+ : rawTarget
1121
+ ) || undefined;
1122
+
1123
+ const pathConfig = normalizePathConfig(
1124
+ rawTarget.caminho !== undefined ? rawTarget.caminho : rawTarget.path
1125
+ );
1126
+
1127
+ if (!position && pathConfig && pathConfig.points.length > 0) {
1128
+ const lastPoint = pathConfig.points[pathConfig.points.length - 1];
1129
+ position = { x: lastPoint.x, y: lastPoint.y };
1130
+ }
1131
+
1132
+ if (!position) {
1133
+ return undefined;
1134
+ }
1135
+
1136
+ return {
1137
+ position,
1138
+ ...(pathConfig ? { path: pathConfig } : {})
1139
+ };
1140
+ }
1141
+
1142
+ function shouldClickOnStep(mode, index, totalPoints) {
1143
+ if (mode === CLICK_MODE.EACH) {
1144
+ return true;
1145
+ }
1146
+
1147
+ if (mode === CLICK_MODE.BETWEEN) {
1148
+ return index < totalPoints - 1;
1149
+ }
1150
+
1151
+ if (mode === CLICK_MODE.FIRST) {
1152
+ return index === 0;
1153
+ }
1154
+
1155
+ if (mode === CLICK_MODE.LAST) {
1156
+ return index === totalPoints - 1;
1157
+ }
1158
+
1159
+ return false;
1160
+ }
1161
+
1162
+ function removeDiacritics(value) {
1163
+ return String(value || '')
1164
+ .normalize('NFD')
1165
+ .replace(/[\u0300-\u036f]/g, '');
1166
+ }
1167
+
1168
+ function canonicalToken(value) {
1169
+ return removeDiacritics(value)
1170
+ .trim()
1171
+ .toLowerCase()
1172
+ .replace(/[^a-z0-9_\-\s]/g, '')
1173
+ .replace(/[-\s]+/g, '_');
1174
+ }
1175
+
1176
+ function normalizeKeyTapToken(value) {
1177
+ const token = canonicalToken(value);
1178
+ if (!token) {
1179
+ return undefined;
1180
+ }
1181
+
1182
+ if (
1183
+ token === 'cmd' ||
1184
+ token === 'command' ||
1185
+ token === 'meta' ||
1186
+ token === 'super' ||
1187
+ token === 'win' ||
1188
+ token === 'windows'
1189
+ ) {
1190
+ return 'command';
1191
+ }
1192
+
1193
+ if (token === 'ctrl' || token === 'control' || token === 'ctl') {
1194
+ return 'control';
1195
+ }
1196
+
1197
+ if (token === 'option' || token === 'opt') {
1198
+ return 'alt';
1199
+ }
1200
+
1201
+ if (token === 'spacebar') {
1202
+ return 'space';
1203
+ }
1204
+
1205
+ return token;
1206
+ }
1207
+
1208
+ function parseKeyModifiers(value) {
1209
+ if (value === undefined || value === null) {
1210
+ return undefined;
1211
+ }
1212
+
1213
+ const list = Array.isArray(value) ? value : [value];
1214
+ const modifiers = [];
1215
+
1216
+ for (const item of list) {
1217
+ if (item === undefined || item === null) {
1218
+ continue;
1219
+ }
1220
+
1221
+ const chunks =
1222
+ typeof item === 'string'
1223
+ ? item.split(/[+|,\s]+/g)
1224
+ : [String(item)];
1225
+
1226
+ for (const chunk of chunks) {
1227
+ const modifier = normalizeKeyTapToken(chunk);
1228
+ if (!modifier) continue;
1229
+ modifiers.push(modifier);
1230
+ }
1231
+ }
1232
+
1233
+ if (modifiers.length === 0) {
1234
+ return undefined;
1235
+ }
1236
+
1237
+ return Array.from(new Set(modifiers));
1238
+ }
1239
+
1240
+ function resolveActionKey(type) {
1241
+ const key = canonicalToken(type);
1242
+ return ACTION_ALIASES[key];
1243
+ }
1244
+
1245
+ function isMapAwareAction(actionKey) {
1246
+ return (
1247
+ actionKey === 'open_app' ||
1248
+ actionKey === 'close_app' ||
1249
+ actionKey === 'skip_ads'
1250
+ );
1251
+ }
1252
+
1253
+ function resolveAppName(name) {
1254
+ const key = canonicalToken(name);
1255
+ return APP_ALIASES[key] || key;
1256
+ }
1257
+
1258
+ function resolveButtonKey(name) {
1259
+ const key = canonicalToken(name);
1260
+ return BUTTON_ALIASES[key] || key;
1261
+ }
1262
+
1263
+ function normalizeState(value) {
1264
+ const key = canonicalToken(value);
1265
+ if (key === 'aberto' || key === 'open' || key === 'opened') {
1266
+ return APP_STATE.OPEN;
1267
+ }
1268
+ if (key === 'fechado' || key === 'closed') {
1269
+ return APP_STATE.CLOSED;
1270
+ }
1271
+ return undefined;
1272
+ }
1273
+
1274
+ function parsePoint(value) {
1275
+ if (Array.isArray(value) && value.length === 2) {
1276
+ const x = Number(value[0]);
1277
+ const y = Number(value[1]);
1278
+ if (Number.isFinite(x) && Number.isFinite(y)) {
1279
+ return { x, y };
1280
+ }
1281
+ }
1282
+
1283
+ if (value && typeof value === 'object') {
1284
+ const x = Number(value.x);
1285
+ const y = Number(value.y);
1286
+ if (Number.isFinite(x) && Number.isFinite(y)) {
1287
+ return { x, y };
1288
+ }
1289
+ }
1290
+
1291
+ return null;
1292
+ }
1293
+
1294
+ function clonePoint(value) {
1295
+ if (!value) return undefined;
1296
+ return { x: value.x, y: value.y };
1297
+ }
1298
+
1299
+ function clonePathConfig(pathConfig) {
1300
+ if (!pathConfig || typeof pathConfig !== 'object') {
1301
+ return undefined;
1302
+ }
1303
+
1304
+ return {
1305
+ points: (pathConfig.points || []).map((point) => clonePoint(point)),
1306
+ clickMode: pathConfig.clickMode || CLICK_MODE.NONE,
1307
+ interval: toNonNegativeInteger(pathConfig.interval) || 0,
1308
+ clickDelay: toNonNegativeInteger(pathConfig.clickDelay) || 0,
1309
+ doubleClick: pathConfig.doubleClick === true
1310
+ };
1311
+ }
1312
+
1313
+ function cloneTargetConfig(targetConfig) {
1314
+ if (!targetConfig || typeof targetConfig !== 'object') {
1315
+ return undefined;
1316
+ }
1317
+
1318
+ const position = targetConfig.position
1319
+ ? clonePoint(targetConfig.position)
1320
+ : clonePoint(parsePoint(targetConfig));
1321
+
1322
+ if (!position) {
1323
+ return undefined;
1324
+ }
1325
+
1326
+ return {
1327
+ position,
1328
+ ...(targetConfig.path ? { path: clonePathConfig(targetConfig.path) } : {})
1329
+ };
1330
+ }
1331
+
1332
+ function cloneButtonMap(buttons) {
1333
+ const cloned = {};
1334
+ if (!buttons || typeof buttons !== 'object') {
1335
+ return cloned;
1336
+ }
1337
+
1338
+ for (const [buttonName, buttonConfig] of Object.entries(buttons)) {
1339
+ if (!buttonConfig || typeof buttonConfig !== 'object') continue;
1340
+ if (!buttonConfig.position) continue;
1341
+ cloned[buttonName] = {
1342
+ position: clonePoint(buttonConfig.position),
1343
+ ...(buttonConfig.path ? { path: clonePathConfig(buttonConfig.path) } : {}),
1344
+ ...(buttonConfig.setState ? { setState: buttonConfig.setState } : {})
1345
+ };
1346
+ }
1347
+
1348
+ return cloned;
1349
+ }
1350
+
1351
+ function cloneAppMap(app) {
1352
+ return {
1353
+ launcher: cloneTargetConfig(app.launcher),
1354
+ searchIcon: cloneTargetConfig(app.searchIcon),
1355
+ state: app.state || APP_STATE.CLOSED,
1356
+ buttons: cloneButtonMap(app.buttons)
1357
+ };
1358
+ }
1359
+
1360
+ async function pathExists(filePath) {
1361
+ try {
1362
+ await readFile(filePath, 'utf8');
1363
+ return true;
1364
+ } catch (error) {
1365
+ if (error && error.code === 'ENOENT') {
1366
+ return false;
1367
+ }
1368
+ throw error;
1369
+ }
1370
+ }
1371
+
1372
+ function shouldIncludeMapDocs(value) {
1373
+ const parsed = parseBooleanValue(value);
1374
+ if (parsed === null) {
1375
+ return value !== false;
1376
+ }
1377
+ return parsed;
1378
+ }
1379
+
1380
+ function resolveDocTemplateByName(value) {
1381
+ const token = canonicalToken(value);
1382
+ if (!token) {
1383
+ return null;
1384
+ }
1385
+
1386
+ if (
1387
+ token === 'doc' ||
1388
+ token === 'doc_en' ||
1389
+ token === 'docmd' ||
1390
+ token === 'en' ||
1391
+ token === 'english'
1392
+ ) {
1393
+ return DOC_TEMPLATE_MAP.doc_en;
1394
+ }
1395
+
1396
+ if (
1397
+ token === 'doc_ptbr' ||
1398
+ token === 'docptbr' ||
1399
+ token === 'ptbr' ||
1400
+ token === 'pt_br' ||
1401
+ token === 'pt'
1402
+ ) {
1403
+ return DOC_TEMPLATE_MAP.doc_ptbr;
1404
+ }
1405
+
1406
+ if (
1407
+ token === 'map' ||
1408
+ token === 'map_en' ||
1409
+ token === 'mapmd' ||
1410
+ token === 'map_doc' ||
1411
+ token === 'mapdoc'
1412
+ ) {
1413
+ return DOC_TEMPLATE_MAP.map_en;
1414
+ }
1415
+
1416
+ if (
1417
+ token === 'map_ptbr' ||
1418
+ token === 'mapptbr' ||
1419
+ token === 'map_pt' ||
1420
+ token === 'map_ptbr_md'
1421
+ ) {
1422
+ return DOC_TEMPLATE_MAP.map_ptbr;
1423
+ }
1424
+
1425
+ return null;
1426
+ }
1427
+
1428
+ function resolveDocTemplates(filesOption, includeMapDocs) {
1429
+ const defaultTemplates = includeMapDocs
1430
+ ? DOC_TEMPLATES
1431
+ : DOC_TEMPLATES.filter((template) => !template.isMapDoc);
1432
+
1433
+ if (filesOption === undefined || filesOption === null) {
1434
+ return defaultTemplates;
1435
+ }
1436
+
1437
+ const values = Array.isArray(filesOption) ? filesOption : [filesOption];
1438
+ const selected = [];
1439
+ const invalid = [];
1440
+
1441
+ for (const value of values) {
1442
+ const template = resolveDocTemplateByName(value);
1443
+ if (!template) {
1444
+ invalid.push(String(value));
1445
+ continue;
1446
+ }
1447
+ if (!includeMapDocs && template.isMapDoc) {
1448
+ continue;
1449
+ }
1450
+ if (!selected.find((item) => item.key === template.key)) {
1451
+ selected.push(template);
1452
+ }
1453
+ }
1454
+
1455
+ if (selected.length === 0 && invalid.length > 0) {
1456
+ throw new Error(`invalid_doc_templates:${invalid.join('|')}`);
1457
+ }
1458
+
1459
+ return selected.length > 0 ? selected : defaultTemplates;
1460
+ }
1461
+
1462
+ function resolveDocOutputDir(options) {
1463
+ const rawOutput =
1464
+ options.outputDir !== undefined
1465
+ ? options.outputDir
1466
+ : options.dir !== undefined
1467
+ ? options.dir
1468
+ : options.path;
1469
+
1470
+ const output = typeof rawOutput === 'string' ? rawOutput.trim() : '';
1471
+ if (!output) {
1472
+ return resolvePath(process.cwd());
1473
+ }
1474
+
1475
+ return resolvePath(output);
1476
+ }
1477
+
1478
+ function normalizeButtonConfig(rawConfig) {
1479
+ if (rawConfig === undefined || rawConfig === null) {
1480
+ return null;
1481
+ }
1482
+
1483
+ const target = normalizeTargetConfig(rawConfig);
1484
+ if (!target) {
1485
+ return null;
1486
+ }
1487
+
1488
+ const rawState =
1489
+ rawConfig && typeof rawConfig === 'object'
1490
+ ? rawConfig.setState !== undefined
1491
+ ? rawConfig.setState
1492
+ : rawConfig.stateAfter !== undefined
1493
+ ? rawConfig.stateAfter
1494
+ : rawConfig.estadoDepois !== undefined
1495
+ ? rawConfig.estadoDepois
1496
+ : undefined
1497
+ : undefined;
1498
+
1499
+ const state = normalizeState(rawState);
1500
+
1501
+ return {
1502
+ position: target.position,
1503
+ ...(target.path ? { path: target.path } : {}),
1504
+ ...(state ? { setState: state } : {})
1505
+ };
1506
+ }
1507
+
1508
+ function mergeTargetPath(target, rawPath) {
1509
+ if (!target || target.path) {
1510
+ return target;
1511
+ }
1512
+
1513
+ const pathConfig = normalizePathConfig(rawPath);
1514
+ if (!pathConfig) {
1515
+ return target;
1516
+ }
1517
+
1518
+ return {
1519
+ ...target,
1520
+ path: pathConfig
1521
+ };
1522
+ }
1523
+
1524
+ function normalizeButtons(rawButtons) {
1525
+ const buttons = {};
1526
+ if (!rawButtons || typeof rawButtons !== 'object') {
1527
+ return buttons;
1528
+ }
1529
+
1530
+ for (const [rawName, rawConfig] of Object.entries(rawButtons)) {
1531
+ const button = normalizeButtonConfig(rawConfig);
1532
+ if (!button) continue;
1533
+
1534
+ const buttonName = resolveButtonKey(rawName);
1535
+ buttons[buttonName] = button;
1536
+ }
1537
+
1538
+ return buttons;
1539
+ }
1540
+
1541
+ function normalizeAppConfig(rawConfig) {
1542
+ if (!rawConfig || typeof rawConfig !== 'object') {
1543
+ return null;
1544
+ }
1545
+
1546
+ const rawLauncher =
1547
+ rawConfig.launcher !== undefined
1548
+ ? rawConfig.launcher
1549
+ : rawConfig.position !== undefined
1550
+ ? rawConfig.position
1551
+ : rawConfig.posicao;
1552
+
1553
+ let launcher = normalizeTargetConfig(rawLauncher);
1554
+ launcher = mergeTargetPath(
1555
+ launcher,
1556
+ rawConfig.caminho !== undefined ? rawConfig.caminho : rawConfig.path
1557
+ );
1558
+
1559
+ const rawSearchIcon =
1560
+ rawConfig.searchIcon !== undefined
1561
+ ? rawConfig.searchIcon
1562
+ : rawConfig.lupa !== undefined
1563
+ ? rawConfig.lupa
1564
+ : rawConfig.search;
1565
+
1566
+ const searchIcon = normalizeTargetConfig(rawSearchIcon);
1567
+
1568
+ const state = normalizeState(
1569
+ rawConfig.state !== undefined ? rawConfig.state : rawConfig.estado
1570
+ );
1571
+
1572
+ const buttons = normalizeButtons(
1573
+ rawConfig.buttons !== undefined ? rawConfig.buttons : rawConfig.botoes
1574
+ );
1575
+
1576
+ return {
1577
+ ...(launcher ? { launcher } : {}),
1578
+ ...(searchIcon ? { searchIcon } : {}),
1579
+ ...(state ? { state } : {}),
1580
+ buttons
1581
+ };
1582
+ }
1583
+
1584
+ function buildFallbackMap(config) {
1585
+ const apps = {};
1586
+
1587
+ for (const [appName, appConfig] of Object.entries(config.apps)) {
1588
+ const normalizedApp = cloneAppMap({
1589
+ ...appConfig,
1590
+ state: appConfig.state || APP_STATE.CLOSED
1591
+ });
1592
+ const buttons = normalizedApp.buttons || {};
1593
+ if (!buttons.fechar) {
1594
+ buttons.fechar = {
1595
+ position: clonePoint(config.defaultClosePoint),
1596
+ setState: APP_STATE.CLOSED
1597
+ };
1598
+ }
1599
+ normalizedApp.buttons = buttons;
1600
+ apps[appName] = normalizedApp;
1601
+ }
1602
+
1603
+ if (apps.youtube) {
1604
+ const buttons = apps.youtube.buttons || {};
1605
+ if (!buttons.pular_ads) {
1606
+ buttons.pular_ads = {
1607
+ position: clonePoint(config.skipAdsPoint)
1608
+ };
1609
+ }
1610
+ apps.youtube.buttons = buttons;
1611
+ }
1612
+
1613
+ return {
1614
+ version: 1,
1615
+ apps
1616
+ };
1617
+ }
1618
+
1619
+ function normalizeMapObject(rawMap) {
1620
+ const result = {
1621
+ version: 1,
1622
+ apps: {}
1623
+ };
1624
+
1625
+ if (!rawMap || typeof rawMap !== 'object') {
1626
+ return result;
1627
+ }
1628
+
1629
+ if (Number.isFinite(rawMap.version)) {
1630
+ result.version = Number(rawMap.version);
1631
+ }
1632
+
1633
+ const rawApps =
1634
+ rawMap.apps !== undefined
1635
+ ? rawMap.apps
1636
+ : rawMap.aplicativos !== undefined
1637
+ ? rawMap.aplicativos
1638
+ : undefined;
1639
+
1640
+ if (!rawApps || typeof rawApps !== 'object') {
1641
+ return result;
1642
+ }
1643
+
1644
+ for (const [rawAppName, rawAppConfig] of Object.entries(rawApps)) {
1645
+ const appName = resolveAppName(rawAppName);
1646
+ const appConfig = normalizeAppConfig(rawAppConfig);
1647
+ if (!appConfig) continue;
1648
+
1649
+ result.apps[appName] = {
1650
+ launcher: appConfig.launcher,
1651
+ searchIcon: appConfig.searchIcon,
1652
+ state: appConfig.state || APP_STATE.CLOSED,
1653
+ buttons: appConfig.buttons || {}
1654
+ };
1655
+ }
1656
+
1657
+ return result;
1658
+ }
1659
+
1660
+ function mergeMaps(baseMap, overrideMap) {
1661
+ const merged = {
1662
+ version:
1663
+ overrideMap && Number.isFinite(overrideMap.version)
1664
+ ? overrideMap.version
1665
+ : baseMap.version,
1666
+ apps: {}
1667
+ };
1668
+
1669
+ for (const [appName, appConfig] of Object.entries(baseMap.apps || {})) {
1670
+ merged.apps[appName] = cloneAppMap(appConfig);
1671
+ }
1672
+
1673
+ for (const [appName, appConfig] of Object.entries(overrideMap.apps || {})) {
1674
+ const current = merged.apps[appName] || {
1675
+ launcher: undefined,
1676
+ searchIcon: undefined,
1677
+ state: APP_STATE.CLOSED,
1678
+ buttons: {}
1679
+ };
1680
+
1681
+ merged.apps[appName] = {
1682
+ launcher: appConfig.launcher || current.launcher,
1683
+ searchIcon: appConfig.searchIcon || current.searchIcon,
1684
+ state: appConfig.state || current.state || APP_STATE.CLOSED,
1685
+ buttons: {
1686
+ ...(current.buttons || {}),
1687
+ ...(appConfig.buttons || {})
1688
+ }
1689
+ };
1690
+ }
1691
+
1692
+ return merged;
1693
+ }
1694
+
1695
+ async function createMapStore(config) {
1696
+ const fallbackMap = buildFallbackMap(config);
1697
+ let fromFileMap = null;
1698
+ let mapCreatedFromMissing = false;
1699
+
1700
+ try {
1701
+ const content = await readFile(config.mapPath, 'utf8');
1702
+ let parsed;
1703
+ try {
1704
+ parsed = JSON.parse(content);
1705
+ } catch (error) {
1706
+ throw new Error(`invalid_map_json:${config.mapPath}`);
1707
+ }
1708
+ fromFileMap = normalizeMapObject(parsed);
1709
+ } catch (error) {
1710
+ if (error && error.code === 'ENOENT') {
1711
+ if (!config.createMapIfMissing) {
1712
+ throw new Error(
1713
+ `map_not_found:${config.mapPath}. Crie o arquivo map.json ou use { createMapIfMissing: true }.`
1714
+ );
1715
+ }
1716
+ fromFileMap = { version: 1, apps: {} };
1717
+ mapCreatedFromMissing = true;
1718
+ } else if (error instanceof Error) {
1719
+ throw error;
1720
+ } else {
1721
+ throw new Error(`map_read_error:${config.mapPath}`);
1722
+ }
1723
+ }
1724
+
1725
+ const map = mergeMaps(fallbackMap, fromFileMap);
1726
+ const store = {
1727
+ path: config.mapPath,
1728
+ persistMap: config.persistMap,
1729
+ map
1730
+ };
1731
+
1732
+ if (config.createMapIfMissing && mapCreatedFromMissing) {
1733
+ await persistMapStore(store);
1734
+ }
1735
+
1736
+ return store;
1737
+ }
1738
+
1739
+ function resolveAppInMap(store, rawAppName) {
1740
+ const appName = resolveAppName(rawAppName);
1741
+ return {
1742
+ appName,
1743
+ app: store.map.apps[appName]
1744
+ };
1745
+ }
1746
+
1747
+ function resolveMappedButton(appConfig, rawButtonName) {
1748
+ if (!rawButtonName) return null;
1749
+ const buttonName = resolveButtonKey(rawButtonName);
1750
+ return appConfig.buttons && appConfig.buttons[buttonName]
1751
+ ? appConfig.buttons[buttonName]
1752
+ : null;
1753
+ }
1754
+
1755
+ async function updateAppState(store, appName, nextState) {
1756
+ const state = normalizeState(nextState);
1757
+ if (!state) return;
1758
+
1759
+ const app = store.map.apps[appName];
1760
+ if (!app) return;
1761
+ if (app.state === state) return;
1762
+
1763
+ app.state = state;
1764
+ await persistMapStore(store);
1765
+ }
1766
+
1767
+ async function persistMapStore(store) {
1768
+ if (!store.persistMap) return;
1769
+ await mkdir(dirnamePath(store.path), { recursive: true });
1770
+ const serialized = JSON.stringify(store.map, null, 2) + '\n';
1771
+ await writeFile(store.path, serialized, 'utf8');
1772
+ }
1773
+
1774
+ function getAppState(appConfig) {
1775
+ return normalizeState(appConfig && appConfig.state) || APP_STATE.CLOSED;
1776
+ }
1777
+
1778
+ function sleep(ms) {
1779
+ return new Promise((resolve) => setTimeout(resolve, ms));
1780
+ }
1781
+
1782
+ async function smoothMove(robot, xFinal, yFinal, steps, delay) {
1783
+ const current = robot.getMousePos();
1784
+ const totalSteps = Math.max(1, parseIntegerOption(steps, 50, 1));
1785
+
1786
+ for (let i = 1; i <= totalSteps; i++) {
1787
+ const progress = i / totalSteps;
1788
+ const x = Math.round(current.x + (xFinal - current.x) * progress);
1789
+ const y = Math.round(current.y + (yFinal - current.y) * progress);
1790
+ robot.moveMouse(x, y);
1791
+ await sleep(delay);
1792
+ }
1793
+ }
1794
+
1795
+ async function smoothScroll(robot, amount, steps, delay) {
1796
+ const totalSteps = Math.max(1, parseIntegerOption(steps, 10, 1));
1797
+ let applied = 0;
1798
+
1799
+ for (let i = 1; i <= totalSteps; i++) {
1800
+ const target = Math.round((amount * i) / totalSteps);
1801
+ const delta = target - applied;
1802
+ if (delta !== 0) {
1803
+ robot.scrollMouse(0, delta);
1804
+ applied = target;
1805
+ }
1806
+ await sleep(delay);
1807
+ }
1808
+ }
1809
+
1810
+ function clickCurrentPosition(robot, doubleClick) {
1811
+ if (doubleClick) {
1812
+ robot.mouseClick('left', true);
1813
+ return;
1814
+ }
1815
+ robot.mouseClick();
1816
+ }
1817
+
1818
+ async function clickPoint(robot, point, config, clickOptions = {}) {
1819
+ await smoothMove(robot, point.x, point.y, config.moveSteps, config.moveDelay);
1820
+ clickCurrentPosition(robot, clickOptions.doubleClick === true);
1821
+ }
1822
+
1823
+ async function runConfiguredPath(robot, pathConfig, config) {
1824
+ if (!pathConfig || !Array.isArray(pathConfig.points) || pathConfig.points.length === 0) {
1825
+ return;
1826
+ }
1827
+
1828
+ const clickMode = normalizeClickMode(pathConfig.clickMode, CLICK_MODE.NONE);
1829
+ const interval = toNonNegativeInteger(pathConfig.interval) || 0;
1830
+ const clickDelay = toNonNegativeInteger(pathConfig.clickDelay) || 0;
1831
+ const doubleClick = pathConfig.doubleClick === true;
1832
+
1833
+ for (let index = 0; index < pathConfig.points.length; index++) {
1834
+ const point = pathConfig.points[index];
1835
+ await smoothMove(robot, point.x, point.y, config.moveSteps, config.moveDelay);
1836
+
1837
+ if (shouldClickOnStep(clickMode, index, pathConfig.points.length)) {
1838
+ if (clickDelay > 0) {
1839
+ await sleep(clickDelay);
1840
+ }
1841
+ clickCurrentPosition(robot, doubleClick);
1842
+ }
1843
+
1844
+ if (index < pathConfig.points.length - 1 && interval > 0) {
1845
+ await sleep(interval);
1846
+ }
1847
+ }
1848
+ }
1849
+
1850
+ async function clickTarget(robot, target, config, clickOptions = {}) {
1851
+ if (!target || !target.position) {
1852
+ throw new Error('invalid_target_config');
1853
+ }
1854
+
1855
+ if (target.path) {
1856
+ await runConfiguredPath(robot, target.path, config);
1857
+ }
1858
+
1859
+ await clickPoint(robot, target.position, config, clickOptions);
1860
+ }
1861
+
1862
+ function createActionHandlers(robot, runtime) {
1863
+ const config = runtime.config;
1864
+ let mapStorePromise;
1865
+
1866
+ async function getMapStore() {
1867
+ if (mapStorePromise) {
1868
+ return mapStorePromise;
1869
+ }
1870
+ mapStorePromise = runtime.getMapStore();
1871
+ return mapStorePromise;
1872
+ }
1873
+
1874
+ async function openAppIfNeeded(appName, appConfig, mapStore) {
1875
+ if (getAppState(appConfig) === APP_STATE.OPEN) {
1876
+ return;
1877
+ }
1878
+
1879
+ if (!appConfig.launcher || !appConfig.launcher.position) {
1880
+ throw new Error(`missing_launcher:${appName}`);
1881
+ }
1882
+
1883
+ if (appConfig.launcher.path) {
1884
+ await clickTarget(robot, appConfig.launcher, config);
1885
+ } else {
1886
+ await smoothMove(robot, 0, 0, config.moveSteps, config.moveDelay);
1887
+ await smoothMove(
1888
+ robot,
1889
+ appConfig.launcher.position.x,
1890
+ appConfig.launcher.position.y,
1891
+ config.moveSteps,
1892
+ config.moveDelay
1893
+ );
1894
+ await smoothMove(
1895
+ robot,
1896
+ appConfig.launcher.position.x + 20,
1897
+ appConfig.launcher.position.y + 10,
1898
+ config.moveSteps,
1899
+ config.moveDelay
1900
+ );
1901
+ await smoothMove(
1902
+ robot,
1903
+ appConfig.launcher.position.x - 20,
1904
+ appConfig.launcher.position.y - 10,
1905
+ config.moveSteps,
1906
+ config.moveDelay
1907
+ );
1908
+ robot.mouseClick();
1909
+ }
1910
+
1911
+ await updateAppState(mapStore, appName, APP_STATE.OPEN);
1912
+ }
1913
+
1914
+ function resolveSequencePoints(command) {
1915
+ const points = normalizePointsValue(
1916
+ command.points !== undefined
1917
+ ? command.points
1918
+ : command.path !== undefined
1919
+ ? command.path
1920
+ : command.command
1921
+ );
1922
+
1923
+ if (!points || points.length < 2) {
1924
+ throw new Error('move_sequence_requires_at_least_2_points');
1925
+ }
1926
+
1927
+ return points;
1928
+ }
1929
+
1930
+ function resolveSequenceInterval(command) {
1931
+ const interval = toNonNegativeInteger(command.interval);
1932
+ if (interval !== null) {
1933
+ return interval;
1934
+ }
1935
+ return config.sequenceInterval;
1936
+ }
1937
+
1938
+ function resolveSequenceClickMode(command) {
1939
+ return normalizeClickMode(command.click, config.sequenceClickMode);
1940
+ }
1941
+
1942
+ function resolveSequenceClickDelay(command) {
1943
+ const clickDelay = toNonNegativeInteger(command.clickDelay);
1944
+ if (clickDelay !== null) {
1945
+ return clickDelay;
1946
+ }
1947
+ return 0;
1948
+ }
1949
+
1950
+ function resolveSequenceDoubleClick(command) {
1951
+ const value = parseBooleanValue(command.doubleClick);
1952
+ if (value !== null) {
1953
+ return value;
1954
+ }
1955
+ return config.sequenceDoubleClick;
1956
+ }
1957
+
1958
+ async function tapKey(key, modifiers) {
1959
+ if (modifiers && modifiers.length > 0) {
1960
+ robot.keyTap(key, modifiers);
1961
+ return;
1962
+ }
1963
+ robot.keyTap(key);
1964
+ }
1965
+
1966
+ function resolveKeyTapPayload(command) {
1967
+ const key = normalizeKeyTapToken(
1968
+ command.key !== undefined ? command.key : command.command
1969
+ );
1970
+
1971
+ if (!key) {
1972
+ throw new Error('key_tap_requires_key');
1973
+ }
1974
+
1975
+ const modifiers =
1976
+ command.modifiers !== undefined
1977
+ ? parseKeyModifiers(command.modifiers)
1978
+ : parseKeyModifiers(command.extra);
1979
+
1980
+ return {
1981
+ key,
1982
+ modifiers
1983
+ };
1984
+ }
1985
+
1986
+ function resolveTypeStringPayload(command) {
1987
+ const text =
1988
+ command.text !== undefined && command.text !== null
1989
+ ? String(command.text)
1990
+ : command.command !== undefined && command.command !== null
1991
+ ? String(command.command)
1992
+ : command.extra !== undefined && command.extra !== null
1993
+ ? String(command.extra)
1994
+ : '';
1995
+
1996
+ if (!text.trim()) {
1997
+ throw new Error('type_string_delayed_requires_text');
1998
+ }
1999
+
2000
+ const customDelay = toNonNegativeInteger(
2001
+ command.typeDelay !== undefined ? command.typeDelay : command.interval
2002
+ );
2003
+
2004
+ return {
2005
+ text,
2006
+ typeDelay: customDelay !== null ? customDelay : config.typeDelay
2007
+ };
2008
+ }
2009
+
2010
+ return {
2011
+ async skip_ads(command) {
2012
+ let point = config.skipAdsPoint;
2013
+
2014
+ if (command.command) {
2015
+ try {
2016
+ const mapStore = await getMapStore();
2017
+ const appRef = resolveAppInMap(mapStore, command.command);
2018
+ const appConfig = appRef.app;
2019
+ if (appConfig) {
2020
+ const button = resolveMappedButton(
2021
+ appConfig,
2022
+ command.button || 'pular_ads'
2023
+ );
2024
+ if (button && button.position) {
2025
+ if (button.path) {
2026
+ await clickTarget(robot, button, config);
2027
+ point = null;
2028
+ } else {
2029
+ point = button.position;
2030
+ }
2031
+ if (button.setState) {
2032
+ await updateAppState(mapStore, appRef.appName, button.setState);
2033
+ }
2034
+ }
2035
+ }
2036
+ } catch (error) {
2037
+ // keep fallback point when map is unavailable for skip_ads
2038
+ }
2039
+ }
2040
+
2041
+ if (point) {
2042
+ await clickPoint(robot, point, config);
2043
+ }
2044
+ },
2045
+
2046
+ async click() {
2047
+ const size = robot.getScreenSize();
2048
+ const centerX = Math.floor(size.width / 2);
2049
+ const centerY = Math.floor(size.height / 2);
2050
+ await clickPoint(robot, { x: centerX, y: centerY }, config);
2051
+ },
2052
+
2053
+ async double_click() {
2054
+ const size = robot.getScreenSize();
2055
+ const centerX = Math.floor(size.width / 2);
2056
+ const centerY = Math.floor(size.height / 2);
2057
+ await clickPoint(
2058
+ robot,
2059
+ { x: centerX, y: centerY },
2060
+ config,
2061
+ { doubleClick: true }
2062
+ );
2063
+ },
2064
+
2065
+ async scroll(command) {
2066
+ const amount = Number(command.command);
2067
+ if (!Number.isFinite(amount)) {
2068
+ throw new Error('scroll_requires_numeric_command');
2069
+ }
2070
+
2071
+ const size = robot.getScreenSize();
2072
+ const centerX = Math.floor(size.width / 2);
2073
+ const centerY = Math.floor(size.height / 2);
2074
+
2075
+ await smoothMove(robot, centerX, centerY, config.moveSteps, config.moveDelay);
2076
+ await smoothScroll(robot, amount, config.scrollSteps, config.scrollDelay);
2077
+ },
2078
+
2079
+ async move_sequence(command) {
2080
+ const points = resolveSequencePoints(command);
2081
+ const interval = resolveSequenceInterval(command);
2082
+ const clickMode = resolveSequenceClickMode(command);
2083
+ const clickDelay = resolveSequenceClickDelay(command);
2084
+ const doubleClick = resolveSequenceDoubleClick(command);
2085
+
2086
+ for (let index = 0; index < points.length; index++) {
2087
+ const point = points[index];
2088
+ await smoothMove(
2089
+ robot,
2090
+ point.x,
2091
+ point.y,
2092
+ config.moveSteps,
2093
+ config.moveDelay
2094
+ );
2095
+
2096
+ if (shouldClickOnStep(clickMode, index, points.length)) {
2097
+ if (clickDelay > 0) {
2098
+ await sleep(clickDelay);
2099
+ }
2100
+ clickCurrentPosition(robot, doubleClick);
2101
+ }
2102
+
2103
+ if (index < points.length - 1 && interval > 0) {
2104
+ await sleep(interval);
2105
+ }
2106
+ }
2107
+ },
2108
+
2109
+ async key_tap(command) {
2110
+ const payload = resolveKeyTapPayload(command);
2111
+ await tapKey(payload.key, payload.modifiers);
2112
+ },
2113
+
2114
+ async type_string_delayed(command) {
2115
+ const payload = resolveTypeStringPayload(command);
2116
+ robot.typeStringDelayed(payload.text, payload.typeDelay);
2117
+ },
2118
+
2119
+ async open_app(command) {
2120
+ await tapKey('command');
2121
+
2122
+ const mapStore = await getMapStore();
2123
+ const appRef = resolveAppInMap(mapStore, command.command);
2124
+ if (!appRef.app) {
2125
+ throw new Error(`unknown_app:${appRef.appName}`);
2126
+ }
2127
+
2128
+ await openAppIfNeeded(appRef.appName, appRef.app, mapStore);
2129
+
2130
+ if (command.button) {
2131
+ const mappedButton = resolveMappedButton(appRef.app, command.button);
2132
+ if (!mappedButton) {
2133
+ throw new Error(
2134
+ `unknown_button:${appRef.appName}:${resolveButtonKey(command.button)}`
2135
+ );
2136
+ }
2137
+
2138
+ await clickTarget(robot, mappedButton, config);
2139
+ if (mappedButton.setState) {
2140
+ await updateAppState(mapStore, appRef.appName, mappedButton.setState);
2141
+ }
2142
+ return;
2143
+ }
2144
+
2145
+ if (command.extra) {
2146
+ if (appRef.app.searchIcon && appRef.app.searchIcon.position) {
2147
+ await clickTarget(robot, appRef.app.searchIcon, config);
2148
+ }
2149
+
2150
+ const customTypeDelay = toNonNegativeInteger(command.typeDelay);
2151
+ const typeDelay =
2152
+ customTypeDelay !== null ? customTypeDelay : config.typeDelay;
2153
+
2154
+ robot.typeStringDelayed(String(command.extra), typeDelay);
2155
+ }
2156
+ },
2157
+
2158
+ async close_app(command) {
2159
+ const mapStore = await getMapStore();
2160
+ const appRef = resolveAppInMap(mapStore, command.command);
2161
+ if (!appRef.app) {
2162
+ throw new Error(`unknown_app:${appRef.appName}`);
2163
+ }
2164
+
2165
+ if (getAppState(appRef.app) === APP_STATE.CLOSED) {
2166
+ return;
2167
+ }
2168
+
2169
+ const mappedButton = resolveMappedButton(
2170
+ appRef.app,
2171
+ command.button || 'fechar'
2172
+ );
2173
+
2174
+ if (mappedButton) {
2175
+ await clickTarget(robot, mappedButton, config);
2176
+ await updateAppState(
2177
+ mapStore,
2178
+ appRef.appName,
2179
+ mappedButton.setState || APP_STATE.CLOSED
2180
+ );
2181
+ return;
2182
+ }
2183
+
2184
+ const modifier =
2185
+ config.launchKey === 'command' ? 'command' : config.launchKey || 'command';
2186
+ robot.keyTap('w', modifier);
2187
+ await updateAppState(mapStore, appRef.appName, APP_STATE.CLOSED);
2188
+ }
2189
+ };
2190
+ }
2191
+
2192
+ async function getRobot() {
2193
+ if (robotCache) {
2194
+ return robotCache;
2195
+ }
2196
+
2197
+ let module;
2198
+ try {
2199
+ module = await import('robotjs');
2200
+ } catch (error) {
2201
+ throw new Error(
2202
+ 'robotjs_not_available: install dependencies and run in a supported desktop environment.'
2203
+ );
2204
+ }
2205
+
2206
+ const robot = module && module.default ? module.default : module;
2207
+ if (!robot || typeof robot.mouseClick !== 'function') {
2208
+ throw new Error(
2209
+ 'robotjs_not_available: install dependencies and run in a supported desktop environment.'
2210
+ );
2211
+ }
2212
+
2213
+ robotCache = robot;
2214
+ return robot;
2215
+ }