command-cmd 1.0.4 → 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.
package/cmd.js CHANGED
@@ -1,8 +1,11 @@
1
1
  import fs from 'fs';
2
2
  import path from 'path';
3
+ import { fileURLToPath } from 'url';
3
4
 
4
- const { readFile, writeFile } = fs.promises;
5
- const { resolve: resolvePath } = path;
5
+ const { readFile, writeFile, mkdir } = fs.promises;
6
+ const { resolve: resolvePath, dirname: dirnamePath } = path;
7
+
8
+ const MODULE_DIR = dirnamePath(fileURLToPath(import.meta.url));
6
9
 
7
10
  const DEFAULT_APPS = Object.freeze({
8
11
  spotify: {
@@ -19,6 +22,35 @@ const DEFAULT_APPS = Object.freeze({
19
22
  }
20
23
  });
21
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
+
22
54
  const ACTION_ALIASES = Object.freeze({
23
55
  skip_ads: 'skip_ads',
24
56
  skipads: 'skip_ads',
@@ -45,7 +77,20 @@ const ACTION_ALIASES = Object.freeze({
45
77
  close_app: 'close_app',
46
78
  closeapp: 'close_app',
47
79
  fechar_app: 'close_app',
48
- fecharapp: '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'
49
94
  });
50
95
 
51
96
  const BUTTON_ALIASES = Object.freeze({
@@ -103,6 +148,10 @@ export function cmd(message) {
103
148
  command: undefined,
104
149
  extra: undefined,
105
150
  button: undefined,
151
+ key: undefined,
152
+ modifiers: undefined,
153
+ text: undefined,
154
+ typeDelay: undefined,
106
155
  points: undefined,
107
156
  path: undefined,
108
157
  interval: undefined,
@@ -140,6 +189,42 @@ export function cmd(message) {
140
189
  command.extra = value;
141
190
  } else if (key === 'button' || key === 'botao' || key === 'botão') {
142
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
+ }
143
228
  } else if (
144
229
  key === 'points' ||
145
230
  key === 'pontos' ||
@@ -200,7 +285,7 @@ export function cmd(message) {
200
285
  }
201
286
  }
202
287
 
203
- if (command.type && command.command) {
288
+ if (hasCommandPayload(command)) {
204
289
  results.push(command);
205
290
  }
206
291
  }
@@ -213,6 +298,86 @@ export function extractFirstCommand(message) {
213
298
  return first || null;
214
299
  }
215
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
+
216
381
  export async function cursor(input, options = {}) {
217
382
  const commands = normalizeCursorInput(input);
218
383
  if (commands.length === 0) {
@@ -313,6 +478,10 @@ export async function cursor(input, options = {}) {
313
478
  command: command.command,
314
479
  extra: command.extra,
315
480
  button: command.button,
481
+ key: command.key,
482
+ modifiers: command.modifiers,
483
+ text: command.text,
484
+ typeDelay: command.typeDelay,
316
485
  points: command.points,
317
486
  path: command.path,
318
487
  interval: command.interval,
@@ -428,6 +597,45 @@ function mergeOptionApps(customApps) {
428
597
  return merged;
429
598
  }
430
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
+
431
639
  function normalizeCommandObject(value) {
432
640
  if (!value || typeof value !== 'object') {
433
641
  return null;
@@ -435,6 +643,28 @@ function normalizeCommandObject(value) {
435
643
 
436
644
  const rawType = value.type !== undefined ? value.type : value.tipo;
437
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;
438
668
  const rawButton =
439
669
  value.button !== undefined
440
670
  ? value.button
@@ -482,37 +712,66 @@ function normalizeCommandObject(value) {
482
712
  : value.cliqueDuplo !== undefined
483
713
  ? value.cliqueDuplo
484
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;
485
731
 
486
732
  const type =
487
733
  typeof rawType === 'string' ? rawType.trim() : String(rawType || '').trim();
734
+ const actionKey = resolveActionKey(type);
488
735
 
489
736
  const normalizedPoints = normalizePointsValue(
490
737
  rawPoints !== undefined ? rawPoints : rawPath
491
738
  );
492
739
 
493
- let command =
494
- typeof rawCommand === 'string'
495
- ? rawCommand.trim()
496
- : Number.isFinite(rawCommand)
497
- ? String(rawCommand)
498
- : '';
740
+ const key = toCommandText(rawKey);
741
+ const text = toCommandText(rawText);
742
+
743
+ let command = toCommandText(rawCommand);
499
744
 
500
745
  if (!command && normalizedPoints) {
501
746
  command = serializePoints(normalizedPoints);
502
747
  }
503
748
 
504
- if (!type || !command) {
505
- return null;
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;
506
764
  }
507
765
 
508
766
  const normalized = {
509
767
  type,
510
768
  command,
511
- extra:
512
- value.extra === undefined || value.extra === null
513
- ? undefined
514
- : String(value.extra),
769
+ extra,
515
770
  button: undefined,
771
+ key: undefined,
772
+ modifiers: undefined,
773
+ text: undefined,
774
+ typeDelay: undefined,
516
775
  points: undefined,
517
776
  path: undefined,
518
777
  interval: undefined,
@@ -529,6 +788,24 @@ function normalizeCommandObject(value) {
529
788
  }
530
789
  }
531
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
+
532
809
  if (normalizedPoints) {
533
810
  normalized.points = normalizedPoints;
534
811
  normalized.path = normalizedPoints;
@@ -562,7 +839,7 @@ function normalizeCommandObject(value) {
562
839
  normalized.seq = seq;
563
840
  }
564
841
 
565
- return normalized;
842
+ return hasCommandPayload(normalized) ? normalized : null;
566
843
  }
567
844
 
568
845
  function normalizeCursorInput(input) {
@@ -896,6 +1173,70 @@ function canonicalToken(value) {
896
1173
  .replace(/[-\s]+/g, '_');
897
1174
  }
898
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
+
899
1240
  function resolveActionKey(type) {
900
1241
  const key = canonicalToken(type);
901
1242
  return ACTION_ALIASES[key];
@@ -1016,6 +1357,124 @@ function cloneAppMap(app) {
1016
1357
  };
1017
1358
  }
1018
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
+
1019
1478
  function normalizeButtonConfig(rawConfig) {
1020
1479
  if (rawConfig === undefined || rawConfig === null) {
1021
1480
  return null;
@@ -1307,6 +1766,7 @@ async function updateAppState(store, appName, nextState) {
1307
1766
 
1308
1767
  async function persistMapStore(store) {
1309
1768
  if (!store.persistMap) return;
1769
+ await mkdir(dirnamePath(store.path), { recursive: true });
1310
1770
  const serialized = JSON.stringify(store.map, null, 2) + '\n';
1311
1771
  await writeFile(store.path, serialized, 'utf8');
1312
1772
  }
@@ -1420,10 +1880,6 @@ function createActionHandlers(robot, runtime) {
1420
1880
  throw new Error(`missing_launcher:${appName}`);
1421
1881
  }
1422
1882
 
1423
- if (config.launchKey) {
1424
- robot.keyTap(config.launchKey);
1425
- }
1426
-
1427
1883
  if (appConfig.launcher.path) {
1428
1884
  await clickTarget(robot, appConfig.launcher, config);
1429
1885
  } else {
@@ -1499,6 +1955,58 @@ function createActionHandlers(robot, runtime) {
1499
1955
  return config.sequenceDoubleClick;
1500
1956
  }
1501
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
+
1502
2010
  return {
1503
2011
  async skip_ads(command) {
1504
2012
  let point = config.skipAdsPoint;
@@ -1598,7 +2106,19 @@ function createActionHandlers(robot, runtime) {
1598
2106
  }
1599
2107
  },
1600
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
+
1601
2119
  async open_app(command) {
2120
+ await tapKey('command');
2121
+
1602
2122
  const mapStore = await getMapStore();
1603
2123
  const appRef = resolveAppInMap(mapStore, command.command);
1604
2124
  if (!appRef.app) {
@@ -1626,7 +2146,12 @@ function createActionHandlers(robot, runtime) {
1626
2146
  if (appRef.app.searchIcon && appRef.app.searchIcon.position) {
1627
2147
  await clickTarget(robot, appRef.app.searchIcon, config);
1628
2148
  }
1629
- robot.typeStringDelayed(String(command.extra), config.typeDelay);
2149
+
2150
+ const customTypeDelay = toNonNegativeInteger(command.typeDelay);
2151
+ const typeDelay =
2152
+ customTypeDelay !== null ? customTypeDelay : config.typeDelay;
2153
+
2154
+ robot.typeStringDelayed(String(command.extra), typeDelay);
1630
2155
  }
1631
2156
  },
1632
2157
 
package/doc.md CHANGED
@@ -7,6 +7,8 @@ Exported functions:
7
7
  - `cmd`
8
8
  - `extractFirstCommand`
9
9
  - `cursor`
10
+ - `initMap`
11
+ - `initDoc`
10
12
 
11
13
  ## Installation
12
14
 
@@ -17,7 +19,7 @@ npm install command-cmd
17
19
  ## Import
18
20
 
19
21
  ```js
20
- import { cmd, extractFirstCommand, cursor } from 'command-cmd';
22
+ import { cmd, extractFirstCommand, cursor, initMap, initDoc } from 'command-cmd';
21
23
  ```
22
24
 
23
25
  ## 1) `cmd(message)`
@@ -61,7 +63,23 @@ Returns only the first valid command.
61
63
  Output:
62
64
 
63
65
  ```js
64
- { type, command, extra, button, points, path, interval, clickDelay, click, doubleClick, seq } | null
66
+ {
67
+ type,
68
+ command,
69
+ extra,
70
+ button,
71
+ key,
72
+ modifiers,
73
+ text,
74
+ typeDelay,
75
+ points,
76
+ path,
77
+ interval,
78
+ clickDelay,
79
+ click,
80
+ doubleClick,
81
+ seq
82
+ } | null
65
83
  ```
66
84
 
67
85
  ## 3) `cursor(input, options?)`
@@ -79,6 +97,10 @@ Accepted object keys:
79
97
  - `type` or `tipo`
80
98
  - `command` or `comando`
81
99
  - `button` or `botao`
100
+ - `key` or `tecla` (for `key_tap`)
101
+ - `modifiers` or `modificadores` (example: `command+shift`)
102
+ - `text` or `texto` (for `type_string_delayed`)
103
+ - `typeDelay` or `typingDelay` (ms per character while typing)
82
104
  - `points` or `pontos` (coordinate sequence)
83
105
  - `path` or `caminho` or `trajeto` (alias of `points`)
84
106
  - `interval` or `intervalo` (ms between points)
@@ -88,6 +110,52 @@ Accepted object keys:
88
110
  - `seq` or `sequencia`
89
111
  - `extra`
90
112
 
113
+ ## 4) `initMap(options?)`
114
+
115
+ Creates (or loads/merges) `map.json` explicitly.
116
+
117
+ Example:
118
+
119
+ ```js
120
+ await initMap();
121
+ await initMap({ mapPath: './config/map.json' });
122
+ ```
123
+
124
+ Optional keys:
125
+
126
+ - `mapPath`
127
+ - `createMapIfMissing`
128
+ - `persistMap`
129
+ - `apps`
130
+ - `defaultClosePoint`
131
+ - `skipAdsPoint`
132
+ - `persistMerged` (writes merged defaults even when map already exists)
133
+
134
+ ## 5) `initDoc(options?)`
135
+
136
+ Creates documentation files from package templates.
137
+
138
+ By default it writes in current working directory:
139
+
140
+ - `doc.md`
141
+ - `docPTBR.md`
142
+ - `map.md`
143
+ - `mapPTBR.md`
144
+
145
+ Example:
146
+
147
+ ```js
148
+ await initDoc();
149
+ await initDoc({ outputDir: './docs', overwrite: true, includeMapDocs: false });
150
+ ```
151
+
152
+ Optional keys:
153
+
154
+ - `outputDir` (alias: `dir`, `path`)
155
+ - `overwrite`
156
+ - `includeMapDocs`
157
+ - `files` (e.g. `['doc', 'docPTBR', 'map', 'mapPTBR']`)
158
+
91
159
  ### App and button names (important)
92
160
 
93
161
  You can name apps and buttons however you want.
@@ -131,10 +199,18 @@ For app commands (`open_app`, `close_app`), the library uses `map.json`:
131
199
  3. Executes mapped button click.
132
200
  4. If that button has `setState`, it updates app state in `map.json`.
133
201
 
202
+ Note: `open_app/abrir_app` always triggers `robot.keyTap("command")` before app flow.
203
+
134
204
  ### Automatic `map.json` creation
135
205
 
136
206
  If `map.json` does not exist, the library creates it automatically in the project root (cwd) by default.
137
207
 
208
+ You can also create it explicitly before any automation:
209
+
210
+ ```js
211
+ await initMap();
212
+ ```
213
+
138
214
  The generated file includes:
139
215
 
140
216
  - `state` for each app;
@@ -162,6 +238,28 @@ AI output example:
162
238
  - `scroll` / `rolar`
163
239
  - `move_sequence` / `mover_sequencia`
164
240
  - `skip_ads` / `pular_ads`
241
+ - `key_tap` / `tecla`
242
+ - `type_string_delayed` / `digitar`
243
+
244
+ ## Keyboard and typing
245
+
246
+ Single key:
247
+
248
+ ```txt
249
+ [key_tap, key: enter]
250
+ ```
251
+
252
+ With modifier:
253
+
254
+ ```txt
255
+ [key_tap, key: v, modifiers: command]
256
+ ```
257
+
258
+ Slow typing:
259
+
260
+ ```txt
261
+ [type_string_delayed, text: Typing slowly..., typeDelay: 100]
262
+ ```
165
263
 
166
264
  ## Sequential movement (new)
167
265
 
@@ -236,7 +334,7 @@ await cursor({
236
334
  mapPath: 'map.json',
237
335
  createMapIfMissing: true,
238
336
  persistMap: true,
239
- launchKey: 'command', // set false to disable launcher key tap
337
+ launchKey: 'command', // used by close_app fallback shortcut (command+w)
240
338
  moveSteps: 50,
241
339
  moveDelay: 8,
242
340
  scrollSteps: 10,
@@ -267,7 +365,7 @@ They explain:
267
365
  ```txt
268
366
  You are a technical assistant.
269
367
  When you suggest executable actions, output only blocks:
270
- [type: <type>, command: <app_or_value>, points: <optional_points>, button: <optional_button>, interval: <optional_ms>, clickDelay: <optional_ms_before_click>, click: <optional_mode>, doubleClick: <optional_boolean>, extra: <optional_text>, seq: <optional_integer>]
368
+ [type: <type>, command: <app_or_value>, key: <optional_key>, modifiers: <optional_modifiers>, text: <optional_text>, typeDelay: <typing_ms>, points: <optional_points>, button: <optional_button>, interval: <optional_ms>, clickDelay: <optional_ms_before_click>, click: <optional_mode>, doubleClick: <optional_boolean>, extra: <optional_text>, seq: <optional_integer>]
271
369
 
272
370
  Rules:
273
371
  1. Always use square brackets.
package/docPTBR.md CHANGED
@@ -7,6 +7,8 @@ Funcoes exportadas:
7
7
  - `cmd`
8
8
  - `extractFirstCommand`
9
9
  - `cursor`
10
+ - `initMap`
11
+ - `initDoc`
10
12
 
11
13
  ## Instalacao
12
14
 
@@ -20,7 +22,9 @@ npm install command-cmd
20
22
  import {
21
23
  cmd as extrairComandos,
22
24
  extractFirstCommand as extrairPrimeiroComando,
23
- cursor as executarCursor
25
+ cursor as executarCursor,
26
+ initMap as iniciarMap,
27
+ initDoc as iniciarDoc
24
28
  } from 'command-cmd';
25
29
  ```
26
30
 
@@ -65,7 +69,23 @@ Retorna apenas o primeiro comando valido.
65
69
  Saida:
66
70
 
67
71
  ```js
68
- { type, command, extra, button, points, path, interval, clickDelay, click, doubleClick, seq } | null
72
+ {
73
+ type,
74
+ command,
75
+ extra,
76
+ button,
77
+ key,
78
+ modifiers,
79
+ text,
80
+ typeDelay,
81
+ points,
82
+ path,
83
+ interval,
84
+ clickDelay,
85
+ click,
86
+ doubleClick,
87
+ seq
88
+ } | null
69
89
  ```
70
90
 
71
91
  ## 3) `cursor(input, options?)`
@@ -83,6 +103,10 @@ Campos aceitos no objeto:
83
103
  - `type` ou `tipo`
84
104
  - `command` ou `comando`
85
105
  - `button` ou `botao`
106
+ - `key` ou `tecla` (para `key_tap`)
107
+ - `modifiers` ou `modificadores` (ex.: `command+shift`)
108
+ - `text` ou `texto` (para `type_string_delayed`)
109
+ - `typeDelay` ou `typingDelay` (ms por caractere na digitacao)
86
110
  - `points` ou `pontos` (sequencia de coordenadas)
87
111
  - `path` ou `caminho` ou `trajeto` (alias de `points`)
88
112
  - `interval` ou `intervalo` (ms entre pontos)
@@ -92,6 +116,52 @@ Campos aceitos no objeto:
92
116
  - `seq` ou `sequencia`
93
117
  - `extra`
94
118
 
119
+ ## 4) `initMap(options?)`
120
+
121
+ Cria (ou carrega/mescla) o `map.json` de forma explicita.
122
+
123
+ Exemplo:
124
+
125
+ ```js
126
+ await iniciarMap();
127
+ await iniciarMap({ mapPath: './config/map.json' });
128
+ ```
129
+
130
+ Campos opcionais:
131
+
132
+ - `mapPath`
133
+ - `createMapIfMissing`
134
+ - `persistMap`
135
+ - `apps`
136
+ - `defaultClosePoint`
137
+ - `skipAdsPoint`
138
+ - `persistMerged` (salva defaults mesclados mesmo se o map ja existir)
139
+
140
+ ## 5) `initDoc(options?)`
141
+
142
+ Cria os arquivos de documentacao usando os templates do pacote.
143
+
144
+ Por padrao, escreve no diretório atual:
145
+
146
+ - `doc.md`
147
+ - `docPTBR.md`
148
+ - `map.md`
149
+ - `mapPTBR.md`
150
+
151
+ Exemplo:
152
+
153
+ ```js
154
+ await iniciarDoc();
155
+ await iniciarDoc({ outputDir: './docs', overwrite: true, includeMapDocs: false });
156
+ ```
157
+
158
+ Campos opcionais:
159
+
160
+ - `outputDir` (alias: `dir`, `path`)
161
+ - `overwrite`
162
+ - `includeMapDocs`
163
+ - `files` (ex.: `['doc', 'docPTBR', 'map', 'mapPTBR']`)
164
+
95
165
  ### Nome dos apps e botoes (importante)
96
166
 
97
167
  Voce pode chamar apps e botoes como quiser.
@@ -135,10 +205,18 @@ Para comandos de app (`abrir_app`, `close_app`), a lib usa `map.json`:
135
205
  3. Executa o clique no botao mapeado.
136
206
  4. Se o botao tiver `setState`, atualiza o estado no `map.json`.
137
207
 
208
+ Obs.: `abrir_app/open_app` sempre dispara `robot.keyTap("command")` antes do fluxo do app.
209
+
138
210
  ### Criacao automatica do `map.json`
139
211
 
140
212
  Se o arquivo `map.json` nao existir, a lib cria automaticamente na raiz do projeto (cwd), por padrao.
141
213
 
214
+ Tambem da para criar explicitamente antes das automacoes:
215
+
216
+ ```js
217
+ await iniciarMap();
218
+ ```
219
+
142
220
  Esse arquivo inicial ja vem com:
143
221
 
144
222
  - `state` em cada app;
@@ -166,6 +244,28 @@ A IA pode converter para:
166
244
  - `scroll` / `rolar`
167
245
  - `mover_sequencia` / `move_sequence`
168
246
  - `skip_ads` / `pular_ads`
247
+ - `key_tap` / `tecla`
248
+ - `type_string_delayed` / `digitar`
249
+
250
+ ## Teclado e digitacao
251
+
252
+ Atalho simples:
253
+
254
+ ```txt
255
+ [key_tap, key: enter]
256
+ ```
257
+
258
+ Atalho com modificador:
259
+
260
+ ```txt
261
+ [key_tap, key: v, modifiers: command]
262
+ ```
263
+
264
+ Digitando devagar:
265
+
266
+ ```txt
267
+ [type_string_delayed, text: Digitando devagar..., typeDelay: 100]
268
+ ```
169
269
 
170
270
  ## Movimento sequencial (novo)
171
271
 
@@ -240,7 +340,7 @@ await executarCursor({
240
340
  mapPath: 'map.json', // caminho do mapeador
241
341
  createMapIfMissing: true, // padrao: cria map automaticamente se faltar
242
342
  persistMap: true, // salva estado no arquivo
243
- launchKey: 'command', // use false para nao disparar tecla de launcher
343
+ launchKey: 'command', // usado no fallback de close_app (atalho command+w)
244
344
  moveSteps: 50,
245
345
  moveDelay: 8,
246
346
  scrollSteps: 10,
@@ -272,7 +372,7 @@ Esses arquivos explicam:
272
372
  ```txt
273
373
  Voce e um assistente tecnico.
274
374
  Quando sugerir acao executavel, responda apenas com blocos:
275
- [tipo: <tipo>, comando: <app_ou_valor>, points: <pontos_opcionais>, botao: <botao_opcional>, interval: <ms_opcional>, clickDelay: <ms_ate_click>, click: <modo_opcional>, doubleClick: <boolean_opcional>, extra: <texto_opcional>, seq: <inteiro_opcional>]
375
+ [tipo: <tipo>, comando: <app_ou_valor>, key: <tecla_opcional>, modifiers: <mods_opcional>, text: <texto_opcional>, typeDelay: <ms_digitacao>, points: <pontos_opcionais>, botao: <botao_opcional>, interval: <ms_opcional>, clickDelay: <ms_ate_click>, click: <modo_opcional>, doubleClick: <boolean_opcional>, extra: <texto_opcional>, seq: <inteiro_opcional>]
276
376
 
277
377
  Regras:
278
378
  1. Use sempre colchetes.
package/map.md CHANGED
@@ -14,6 +14,12 @@ Create `map.json` in your project root (where Node runs).
14
14
 
15
15
  By default, the library auto-creates this file if it does not exist.
16
16
 
17
+ You can also create it explicitly:
18
+
19
+ ```js
20
+ await initMap();
21
+ ```
22
+
17
23
  If you need another path:
18
24
 
19
25
  ```js
package/mapPTBR.md CHANGED
@@ -14,6 +14,12 @@ Crie `map.json` na raiz do seu projeto (mesma pasta onde voce roda o Node).
14
14
 
15
15
  Por padrao, a lib cria esse arquivo automaticamente se ele nao existir.
16
16
 
17
+ Tambem da para criar explicitamente:
18
+
19
+ ```js
20
+ await initMap();
21
+ ```
22
+
17
23
  Se quiser outro caminho, passe em `cursor`:
18
24
 
19
25
  ```js
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "command-cmd",
3
- "version": "1.0.4",
3
+ "version": "1.0.5",
4
4
  "main": "cmd.js",
5
5
  "type": "module",
6
6
  "scripts": {