@monoes/monomindcli 1.10.30 → 1.10.31

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 (48) hide show
  1. package/.claude/helpers/handlers/adr-draft-handler.cjs +64 -0
  2. package/.claude/helpers/handlers/agent-start-handler.cjs +99 -0
  3. package/.claude/helpers/handlers/graph-status-handler.cjs +38 -0
  4. package/.claude/helpers/handlers/session-restore-handler.cjs +45 -40
  5. package/.claude/helpers/hook-handler.cjs +6 -158
  6. package/dist/src/browser/actions.d.ts +15 -0
  7. package/dist/src/browser/actions.d.ts.map +1 -1
  8. package/dist/src/browser/actions.js +91 -0
  9. package/dist/src/browser/actions.js.map +1 -1
  10. package/dist/src/browser/batch.d.ts +13 -0
  11. package/dist/src/browser/batch.d.ts.map +1 -0
  12. package/dist/src/browser/batch.js +11 -0
  13. package/dist/src/browser/batch.js.map +1 -0
  14. package/dist/src/browser/console-log.d.ts +22 -0
  15. package/dist/src/browser/console-log.d.ts.map +1 -0
  16. package/dist/src/browser/console-log.js +55 -0
  17. package/dist/src/browser/console-log.js.map +1 -0
  18. package/dist/src/browser/dialog.d.ts +11 -0
  19. package/dist/src/browser/dialog.d.ts.map +1 -0
  20. package/dist/src/browser/dialog.js +36 -0
  21. package/dist/src/browser/dialog.js.map +1 -0
  22. package/dist/src/browser/emulation.d.ts +15 -0
  23. package/dist/src/browser/emulation.d.ts.map +1 -0
  24. package/dist/src/browser/emulation.js +62 -0
  25. package/dist/src/browser/emulation.js.map +1 -0
  26. package/dist/src/browser/find.d.ts +21 -0
  27. package/dist/src/browser/find.d.ts.map +1 -0
  28. package/dist/src/browser/find.js +118 -0
  29. package/dist/src/browser/find.js.map +1 -0
  30. package/dist/src/browser/index.d.ts +7 -0
  31. package/dist/src/browser/index.d.ts.map +1 -1
  32. package/dist/src/browser/index.js +7 -0
  33. package/dist/src/browser/index.js.map +1 -1
  34. package/dist/src/browser/pdf.d.ts +15 -0
  35. package/dist/src/browser/pdf.d.ts.map +1 -0
  36. package/dist/src/browser/pdf.js +27 -0
  37. package/dist/src/browser/pdf.js.map +1 -0
  38. package/dist/src/browser/storage.d.ts +11 -0
  39. package/dist/src/browser/storage.d.ts.map +1 -0
  40. package/dist/src/browser/storage.js +43 -0
  41. package/dist/src/browser/storage.js.map +1 -0
  42. package/dist/src/commands/browse.d.ts.map +1 -1
  43. package/dist/src/commands/browse.js +939 -18
  44. package/dist/src/commands/browse.js.map +1 -1
  45. package/dist/src/ui/dashboard-v2.html +182 -17
  46. package/dist/src/ui/server.mjs +56 -0
  47. package/dist/tsconfig.tsbuildinfo +1 -1
  48. package/package.json +1 -1
@@ -318,13 +318,13 @@ const navigateCommand = {
318
318
  };
319
319
  const setCommand = {
320
320
  name: 'set',
321
- description: 'Configure browser settings. Usage: monomind browse set viewport 375 812',
321
+ description: 'Configure browser settings. Usage: monomind browse set viewport|device|geo|offline|media|credentials|useragent <args>',
322
322
  action: async (ctx) => {
323
323
  const { client, sessionId } = await ensureConnected(_port);
324
324
  const browser = await getBrowser();
325
325
  const setting = ctx.args[0];
326
326
  if (!setting)
327
- throw new Error('Usage: monomind browse set viewport <width> <height>');
327
+ throw new Error('Usage: monomind browse set viewport|device|geo|offline|media|credentials|useragent <args>');
328
328
  switch (setting) {
329
329
  case 'viewport': {
330
330
  const width = parseInt(ctx.args[1], 10);
@@ -335,6 +335,47 @@ const setCommand = {
335
335
  output.printSuccess(`Viewport set to ${width}x${height}`);
336
336
  break;
337
337
  }
338
+ case 'device': {
339
+ const deviceName = ctx.args[1];
340
+ if (!deviceName)
341
+ throw new Error(`Usage: set device <name>. Available: ${browser.listDevices().join(', ')}`);
342
+ await browser.emulateDevice(client, sessionId, deviceName);
343
+ output.printSuccess(`Emulating device: ${deviceName}`);
344
+ break;
345
+ }
346
+ case 'geo': {
347
+ const lat = parseFloat(ctx.args[1]);
348
+ const lon = parseFloat(ctx.args[2]);
349
+ const acc = parseFloat(ctx.args[3]) || 100;
350
+ if (isNaN(lat) || isNaN(lon))
351
+ throw new Error('Usage: set geo <latitude> <longitude> [accuracy]');
352
+ await browser.setGeolocation(client, sessionId, lat, lon, acc);
353
+ output.printSuccess(`Geolocation set: ${lat}, ${lon}`);
354
+ break;
355
+ }
356
+ case 'offline': {
357
+ const enabled = ctx.args[1] !== 'false';
358
+ await browser.setOfflineMode(client, sessionId, enabled);
359
+ output.printSuccess(`Offline mode: ${enabled}`);
360
+ break;
361
+ }
362
+ case 'media': {
363
+ const scheme = ctx.args[1];
364
+ if (!scheme)
365
+ throw new Error('Usage: set media dark|light|no-preference');
366
+ await browser.setColorScheme(client, sessionId, scheme);
367
+ output.printSuccess(`Color scheme: ${scheme}`);
368
+ break;
369
+ }
370
+ case 'credentials': {
371
+ const username = ctx.args[1];
372
+ const password = ctx.args[2];
373
+ if (!username || !password)
374
+ throw new Error('Usage: set credentials <username> <password>');
375
+ await browser.setBasicAuth(client, sessionId, username, password);
376
+ output.printSuccess('Basic auth credentials set');
377
+ break;
378
+ }
338
379
  case 'useragent': {
339
380
  const ua = ctx.args[1];
340
381
  if (!ua)
@@ -344,7 +385,7 @@ const setCommand = {
344
385
  break;
345
386
  }
346
387
  default:
347
- throw new Error(`Unknown setting: ${setting}. Use: viewport|useragent`);
388
+ throw new Error(`Unknown setting: ${setting}. Use: viewport|device|geo|offline|media|credentials|useragent`);
348
389
  }
349
390
  return { success: true };
350
391
  },
@@ -500,6 +541,826 @@ const closeCommand = {
500
541
  },
501
542
  };
502
543
  // ---------------------------------------------------------------------------
544
+ // Additional subcommands
545
+ // ---------------------------------------------------------------------------
546
+ const dblclickCommand = {
547
+ name: 'dblclick',
548
+ description: 'Double-click an element. Usage: monomind browse dblclick @e1',
549
+ action: async (ctx) => {
550
+ const { client, sessionId } = await ensureConnected(_port);
551
+ const browser = await getBrowser();
552
+ const refArg = ctx.args[0];
553
+ if (!refArg)
554
+ throw new Error('Usage: monomind browse dblclick @e1');
555
+ const refKey = refArg.startsWith('@') ? refArg.slice(1) : refArg;
556
+ const ref = await browser.resolveRef(client, sessionId, _refs, refKey);
557
+ await browser.clickElement(client, sessionId, ref, { clickCount: 2 });
558
+ output.printSuccess(`Double-clicked: ${ref.role} "${ref.name}"`);
559
+ return { success: true };
560
+ },
561
+ };
562
+ const focusCommand = {
563
+ name: 'focus',
564
+ description: 'Focus an element. Usage: monomind browse focus @e1',
565
+ action: async (ctx) => {
566
+ const { client, sessionId } = await ensureConnected(_port);
567
+ const browser = await getBrowser();
568
+ const refArg = ctx.args[0];
569
+ if (!refArg)
570
+ throw new Error('Usage: monomind browse focus @e1');
571
+ const refKey = refArg.startsWith('@') ? refArg.slice(1) : refArg;
572
+ const ref = await browser.resolveRef(client, sessionId, _refs, refKey);
573
+ await browser.focusElement(client, sessionId, ref);
574
+ output.printSuccess(`Focused: ${ref.role} "${ref.name}"`);
575
+ return { success: true };
576
+ },
577
+ };
578
+ const typeCommand = {
579
+ name: 'type',
580
+ description: 'Type text into element (appends, does not clear). Usage: monomind browse type @e1 "text"',
581
+ action: async (ctx) => {
582
+ const { client, sessionId } = await ensureConnected(_port);
583
+ const browser = await getBrowser();
584
+ const refArg = ctx.args[0];
585
+ const value = ctx.args[1];
586
+ if (!refArg || value === undefined)
587
+ throw new Error('Usage: monomind browse type @e1 "value"');
588
+ const refKey = refArg.startsWith('@') ? refArg.slice(1) : refArg;
589
+ const ref = await browser.resolveRef(client, sessionId, _refs, refKey);
590
+ await browser.typeIntoElement(client, sessionId, ref, value);
591
+ output.printSuccess(`Typed into: ${ref.role} "${ref.name}"`);
592
+ return { success: true };
593
+ },
594
+ };
595
+ const keyboardCommand = {
596
+ name: 'keyboard',
597
+ description: 'Keyboard commands. Usage: monomind browse keyboard type "text" | inserttext "text"',
598
+ action: async (ctx) => {
599
+ const { client, sessionId } = await ensureConnected(_port);
600
+ const browser = await getBrowser();
601
+ const action = ctx.args[0];
602
+ const text = ctx.args[1];
603
+ if (!action || !text)
604
+ throw new Error('Usage: monomind browse keyboard type|inserttext "text"');
605
+ await browser.typeText(client, sessionId, text);
606
+ output.printSuccess(`Keyboard ${action}: ${text.length} chars`);
607
+ return { success: true };
608
+ },
609
+ };
610
+ const keydownCommand = {
611
+ name: 'keydown',
612
+ description: 'Hold key down. Usage: monomind browse keydown Shift',
613
+ action: async (ctx) => {
614
+ const { client, sessionId } = await ensureConnected(_port);
615
+ const browser = await getBrowser();
616
+ const key = ctx.args[0];
617
+ if (!key)
618
+ throw new Error('Usage: monomind browse keydown <key>');
619
+ await browser.keyDown(client, sessionId, key);
620
+ output.printSuccess(`Key down: ${key}`);
621
+ return { success: true };
622
+ },
623
+ };
624
+ const keyupCommand = {
625
+ name: 'keyup',
626
+ description: 'Release held key. Usage: monomind browse keyup Shift',
627
+ action: async (ctx) => {
628
+ const { client, sessionId } = await ensureConnected(_port);
629
+ const browser = await getBrowser();
630
+ const key = ctx.args[0];
631
+ if (!key)
632
+ throw new Error('Usage: monomind browse keyup <key>');
633
+ await browser.keyUp(client, sessionId, key);
634
+ output.printSuccess(`Key up: ${key}`);
635
+ return { success: true };
636
+ },
637
+ };
638
+ const hoverCommand = {
639
+ name: 'hover',
640
+ description: 'Hover over an element. Usage: monomind browse hover @e1',
641
+ action: async (ctx) => {
642
+ const { client, sessionId } = await ensureConnected(_port);
643
+ const browser = await getBrowser();
644
+ const refArg = ctx.args[0];
645
+ if (!refArg)
646
+ throw new Error('Usage: monomind browse hover @e1');
647
+ const refKey = refArg.startsWith('@') ? refArg.slice(1) : refArg;
648
+ const ref = await browser.resolveRef(client, sessionId, _refs, refKey);
649
+ await browser.hoverElement(client, sessionId, ref);
650
+ output.printSuccess(`Hovered: ${ref.role} "${ref.name}"`);
651
+ return { success: true };
652
+ },
653
+ };
654
+ const selectCommand = {
655
+ name: 'select',
656
+ description: 'Select a dropdown option. Usage: monomind browse select @e1 "Option text"',
657
+ action: async (ctx) => {
658
+ const { client, sessionId } = await ensureConnected(_port);
659
+ const browser = await getBrowser();
660
+ const refArg = ctx.args[0];
661
+ const value = ctx.args[1];
662
+ if (!refArg || !value)
663
+ throw new Error('Usage: monomind browse select @e1 "value"');
664
+ const refKey = refArg.startsWith('@') ? refArg.slice(1) : refArg;
665
+ const ref = await browser.resolveRef(client, sessionId, _refs, refKey);
666
+ await browser.selectOption(client, sessionId, ref, value);
667
+ output.printSuccess(`Selected: "${value}"`);
668
+ return { success: true };
669
+ },
670
+ };
671
+ const checkCommand = {
672
+ name: 'check',
673
+ description: 'Check a checkbox. Usage: monomind browse check @e1',
674
+ action: async (ctx) => {
675
+ const { client, sessionId } = await ensureConnected(_port);
676
+ const browser = await getBrowser();
677
+ const refArg = ctx.args[0];
678
+ if (!refArg)
679
+ throw new Error('Usage: monomind browse check @e1');
680
+ const refKey = refArg.startsWith('@') ? refArg.slice(1) : refArg;
681
+ const ref = await browser.resolveRef(client, sessionId, _refs, refKey);
682
+ await browser.checkElement(client, sessionId, ref, true);
683
+ output.printSuccess(`Checked: ${ref.role} "${ref.name}"`);
684
+ return { success: true };
685
+ },
686
+ };
687
+ const uncheckCommand = {
688
+ name: 'uncheck',
689
+ description: 'Uncheck a checkbox. Usage: monomind browse uncheck @e1',
690
+ action: async (ctx) => {
691
+ const { client, sessionId } = await ensureConnected(_port);
692
+ const browser = await getBrowser();
693
+ const refArg = ctx.args[0];
694
+ if (!refArg)
695
+ throw new Error('Usage: monomind browse uncheck @e1');
696
+ const refKey = refArg.startsWith('@') ? refArg.slice(1) : refArg;
697
+ const ref = await browser.resolveRef(client, sessionId, _refs, refKey);
698
+ await browser.checkElement(client, sessionId, ref, false);
699
+ output.printSuccess(`Unchecked: ${ref.role} "${ref.name}"`);
700
+ return { success: true };
701
+ },
702
+ };
703
+ const scrollIntoViewCommand = {
704
+ name: 'scrollintoview',
705
+ description: 'Scroll element into view. Usage: monomind browse scrollintoview @e1',
706
+ action: async (ctx) => {
707
+ const { client, sessionId } = await ensureConnected(_port);
708
+ const browser = await getBrowser();
709
+ const refArg = ctx.args[0];
710
+ if (!refArg)
711
+ throw new Error('Usage: monomind browse scrollintoview @e1');
712
+ const refKey = refArg.startsWith('@') ? refArg.slice(1) : refArg;
713
+ const ref = await browser.resolveRef(client, sessionId, _refs, refKey);
714
+ await browser.scrollIntoView(client, sessionId, ref);
715
+ output.printSuccess(`Scrolled into view: ${ref.role} "${ref.name}"`);
716
+ return { success: true };
717
+ },
718
+ };
719
+ const dragCommand = {
720
+ name: 'drag',
721
+ description: 'Drag element to another element. Usage: monomind browse drag @e1 @e2',
722
+ action: async (ctx) => {
723
+ const { client, sessionId } = await ensureConnected(_port);
724
+ const browser = await getBrowser();
725
+ const srcArg = ctx.args[0];
726
+ const tgtArg = ctx.args[1];
727
+ if (!srcArg || !tgtArg)
728
+ throw new Error('Usage: monomind browse drag @e1 @e2');
729
+ const srcKey = srcArg.startsWith('@') ? srcArg.slice(1) : srcArg;
730
+ const tgtKey = tgtArg.startsWith('@') ? tgtArg.slice(1) : tgtArg;
731
+ const src = await browser.resolveRef(client, sessionId, _refs, srcKey);
732
+ const tgt = await browser.resolveRef(client, sessionId, _refs, tgtKey);
733
+ await browser.dragAndDrop(client, sessionId, src, tgt);
734
+ output.printSuccess(`Dragged @${srcKey} to @${tgtKey}`);
735
+ return { success: true };
736
+ },
737
+ };
738
+ const uploadCommand = {
739
+ name: 'upload',
740
+ description: 'Upload files to a file input. Usage: monomind browse upload @e1 ./file.pdf',
741
+ action: async (ctx) => {
742
+ const { client, sessionId } = await ensureConnected(_port);
743
+ const browser = await getBrowser();
744
+ const refArg = ctx.args[0];
745
+ const files = ctx.args.slice(1);
746
+ if (!refArg || files.length === 0)
747
+ throw new Error('Usage: monomind browse upload @e1 <file1> [file2...]');
748
+ const refKey = refArg.startsWith('@') ? refArg.slice(1) : refArg;
749
+ const ref = await browser.resolveRef(client, sessionId, _refs, refKey);
750
+ await browser.uploadFile(client, sessionId, ref, files);
751
+ output.printSuccess(`Uploaded ${files.length} file(s) to @${refKey}`);
752
+ return { success: true };
753
+ },
754
+ };
755
+ const mouseCommand = {
756
+ name: 'mouse',
757
+ description: 'Fine-grained mouse control. Usage: monomind browse mouse move|down|up|wheel <args>',
758
+ options: [
759
+ { name: 'button', type: 'string', description: 'Button: left|right|middle', default: 'left' },
760
+ ],
761
+ action: async (ctx) => {
762
+ const { client, sessionId } = await ensureConnected(_port);
763
+ const browser = await getBrowser();
764
+ const action = ctx.args[0];
765
+ switch (action) {
766
+ case 'move': {
767
+ const x = parseFloat(ctx.args[1]);
768
+ const y = parseFloat(ctx.args[2]);
769
+ await browser.mouseMove(client, sessionId, x, y);
770
+ output.printSuccess(`Mouse moved to (${x}, ${y})`);
771
+ break;
772
+ }
773
+ case 'down': {
774
+ const x = parseFloat(ctx.args[1]) || 0;
775
+ const y = parseFloat(ctx.args[2]) || 0;
776
+ const button = ctx.flags.button ?? 'left';
777
+ await browser.mouseDown(client, sessionId, x, y, button);
778
+ output.printSuccess(`Mouse down at (${x}, ${y})`);
779
+ break;
780
+ }
781
+ case 'up': {
782
+ const x = parseFloat(ctx.args[1]) || 0;
783
+ const y = parseFloat(ctx.args[2]) || 0;
784
+ const button = ctx.flags.button ?? 'left';
785
+ await browser.mouseUp(client, sessionId, x, y, button);
786
+ output.printSuccess(`Mouse up at (${x}, ${y})`);
787
+ break;
788
+ }
789
+ case 'wheel': {
790
+ const x = parseFloat(ctx.args[1]) || 0;
791
+ const y = parseFloat(ctx.args[2]) || 0;
792
+ const dy = parseFloat(ctx.args[3]) || 0;
793
+ const dx = parseFloat(ctx.args[4]) || 0;
794
+ await browser.mouseWheel(client, sessionId, x, y, dy, dx);
795
+ output.printSuccess(`Mouse wheel (${dx}, ${dy})`);
796
+ break;
797
+ }
798
+ default:
799
+ throw new Error('Usage: monomind browse mouse move|down|up|wheel <args>');
800
+ }
801
+ return { success: true };
802
+ },
803
+ };
804
+ const clipboardCommand = {
805
+ name: 'clipboard',
806
+ description: 'Clipboard operations. Usage: monomind browse clipboard read|write|copy|paste',
807
+ action: async (ctx) => {
808
+ const { client, sessionId } = await ensureConnected(_port);
809
+ const browser = await getBrowser();
810
+ const action = ctx.args[0];
811
+ switch (action) {
812
+ case 'read': {
813
+ const text = await browser.readClipboard(client, sessionId);
814
+ print(text);
815
+ return { success: true, data: { text } };
816
+ }
817
+ case 'write': {
818
+ const text = ctx.args[1];
819
+ if (!text)
820
+ throw new Error('Usage: monomind browse clipboard write "text"');
821
+ await browser.writeClipboard(client, sessionId, text);
822
+ output.printSuccess('Clipboard written');
823
+ break;
824
+ }
825
+ case 'copy':
826
+ await browser.pressKey(client, sessionId, 'c');
827
+ output.printSuccess('Copy sent (Ctrl+C equivalent)');
828
+ break;
829
+ case 'paste':
830
+ await browser.pressKey(client, sessionId, 'v');
831
+ output.printSuccess('Paste sent (Ctrl+V equivalent)');
832
+ break;
833
+ default:
834
+ throw new Error('Usage: monomind browse clipboard read|write|copy|paste');
835
+ }
836
+ return { success: true };
837
+ },
838
+ };
839
+ const dialogCommand = {
840
+ name: 'dialog',
841
+ description: 'Handle browser dialogs. Usage: monomind browse dialog accept|dismiss|status',
842
+ action: async (ctx) => {
843
+ const { client, sessionId } = await ensureConnected(_port);
844
+ const browser = await getBrowser();
845
+ const action = ctx.args[0];
846
+ switch (action) {
847
+ case 'accept': {
848
+ const text = ctx.args[1];
849
+ await browser.acceptDialog(client, sessionId, text);
850
+ output.printSuccess('Dialog accepted');
851
+ break;
852
+ }
853
+ case 'dismiss':
854
+ await browser.dismissDialog(client, sessionId);
855
+ output.printSuccess('Dialog dismissed');
856
+ break;
857
+ case 'status': {
858
+ const info = browser.getDialogStatus();
859
+ if (info) {
860
+ print(`Dialog open: type=${info.type} message="${info.message}"`);
861
+ }
862
+ else {
863
+ print('No dialog open');
864
+ }
865
+ return { success: true, data: { dialog: info } };
866
+ }
867
+ default:
868
+ throw new Error('Usage: monomind browse dialog accept|dismiss|status');
869
+ }
870
+ return { success: true };
871
+ },
872
+ };
873
+ const frameCommand = {
874
+ name: 'frame',
875
+ description: 'Switch to iframe or back to main. Usage: monomind browse frame "#frame-id" | frame main',
876
+ action: async (ctx) => {
877
+ const { client, sessionId } = await ensureConnected(_port);
878
+ const browser = await getBrowser();
879
+ const target = ctx.args[0];
880
+ if (!target)
881
+ throw new Error('Usage: monomind browse frame <selector>|main');
882
+ if (target === 'main') {
883
+ output.printSuccess('Switched to main frame');
884
+ }
885
+ else {
886
+ const frameSrc = await browser.switchToFrame(client, sessionId, target);
887
+ output.printSuccess(`Switched to frame: ${frameSrc ?? target}`);
888
+ }
889
+ return { success: true };
890
+ },
891
+ };
892
+ const tabCommand = {
893
+ name: 'tab',
894
+ description: 'Tab management. Usage: monomind browse tab list|new|close [url]',
895
+ options: [
896
+ { name: 'label', type: 'string', description: 'Label for new tab' },
897
+ ],
898
+ action: async (ctx) => {
899
+ const { client, sessionId } = await ensureConnected(_port);
900
+ const browser = await getBrowser();
901
+ const action = ctx.args[0];
902
+ switch (action ?? 'list') {
903
+ case 'list': {
904
+ const tabs = await browser.listTabs(_port);
905
+ for (const t of tabs)
906
+ print(` ${t.id}: ${t.title} (${t.url})`);
907
+ return { success: true, data: { tabs } };
908
+ }
909
+ case 'new': {
910
+ const url = ctx.args[1];
911
+ const tab = await browser.newTab(_port, url);
912
+ output.printSuccess(`New tab: ${tab.id} ${url ?? ''}`);
913
+ return { success: true, data: { tab } };
914
+ }
915
+ case 'close': {
916
+ const tabId = ctx.args[1];
917
+ if (!tabId)
918
+ throw new Error('Usage: monomind browse tab close <tabId>');
919
+ await browser.closeTab(client, sessionId, tabId);
920
+ output.printSuccess(`Closed tab: ${tabId}`);
921
+ break;
922
+ }
923
+ default: {
924
+ // Try to treat arg as tab ID to switch to
925
+ await browser.activateTab(client, sessionId, action);
926
+ output.printSuccess(`Switched to tab: ${action}`);
927
+ }
928
+ }
929
+ return { success: true };
930
+ },
931
+ };
932
+ const consoleLogCommand = {
933
+ name: 'console',
934
+ description: 'View captured console messages. Usage: monomind browse console [--clear] [--json]',
935
+ options: [
936
+ { name: 'clear', type: 'boolean', description: 'Clear console messages', default: false },
937
+ { name: 'json', type: 'boolean', description: 'Output as JSON', default: false },
938
+ { name: 'errors-only', type: 'boolean', description: 'Show only errors', default: false },
939
+ ],
940
+ action: async (ctx) => {
941
+ const browser = await getBrowser();
942
+ if (ctx.flags.clear) {
943
+ browser.clearConsoleMessages();
944
+ output.printSuccess('Console cleared');
945
+ return { success: true };
946
+ }
947
+ const msgs = browser.getConsoleMessages();
948
+ if (ctx.flags.json) {
949
+ print(JSON.stringify(msgs));
950
+ }
951
+ else {
952
+ for (const m of msgs) {
953
+ const prefix = m.type === 'error' ? '[ERROR]' : m.type === 'warn' ? '[WARN]' : '[LOG]';
954
+ print(`${prefix} ${m.text}`);
955
+ }
956
+ }
957
+ return { success: true, data: { messages: msgs } };
958
+ },
959
+ };
960
+ const errorsCommand = {
961
+ name: 'errors',
962
+ description: 'View page errors (uncaught JS exceptions). Usage: monomind browse errors [--clear]',
963
+ options: [
964
+ { name: 'clear', type: 'boolean', description: 'Clear errors', default: false },
965
+ { name: 'json', type: 'boolean', description: 'Output as JSON', default: false },
966
+ ],
967
+ action: async (ctx) => {
968
+ const browser = await getBrowser();
969
+ if (ctx.flags.clear) {
970
+ browser.clearPageErrors();
971
+ output.printSuccess('Errors cleared');
972
+ return { success: true };
973
+ }
974
+ const errs = browser.getPageErrors();
975
+ if (ctx.flags.json) {
976
+ print(JSON.stringify(errs));
977
+ }
978
+ else if (errs.length === 0) {
979
+ output.printSuccess('No page errors');
980
+ }
981
+ else {
982
+ for (const e of errs)
983
+ print(`[ERROR] ${e.text} (${e.url}:${e.lineNumber})`);
984
+ }
985
+ return { success: true, data: { errors: errs } };
986
+ },
987
+ };
988
+ const storageCommand = {
989
+ name: 'storage',
990
+ description: 'localStorage/sessionStorage management. Usage: monomind browse storage local|session [key] [--set val] [--clear]',
991
+ options: [
992
+ { name: 'set', type: 'string', description: 'Value to set for key' },
993
+ { name: 'clear', type: 'boolean', description: 'Clear all storage', default: false },
994
+ { name: 'remove', type: 'boolean', description: 'Remove a specific key', default: false },
995
+ { name: 'json', type: 'boolean', description: 'Output as JSON', default: false },
996
+ ],
997
+ action: async (ctx) => {
998
+ const { client, sessionId } = await ensureConnected(_port);
999
+ const browser = await getBrowser();
1000
+ const storageType = ctx.args[0];
1001
+ const key = ctx.args[1];
1002
+ if (!storageType)
1003
+ throw new Error('Usage: monomind browse storage local|session [key]');
1004
+ const isLocal = storageType === 'local';
1005
+ if (ctx.flags.clear) {
1006
+ if (isLocal)
1007
+ await browser.clearLocalStorage(client, sessionId);
1008
+ else
1009
+ await browser.clearSessionStorage(client, sessionId);
1010
+ output.printSuccess(`${storageType}Storage cleared`);
1011
+ return { success: true };
1012
+ }
1013
+ if (key && ctx.flags.set !== undefined) {
1014
+ if (isLocal)
1015
+ await browser.setLocalStorageKey(client, sessionId, key, ctx.flags.set);
1016
+ else
1017
+ await browser.setSessionStorageKey(client, sessionId, key, ctx.flags.set);
1018
+ output.printSuccess(`Set ${key}`);
1019
+ return { success: true };
1020
+ }
1021
+ if (key && ctx.flags.remove) {
1022
+ if (isLocal)
1023
+ await browser.removeLocalStorageKey(client, sessionId, key);
1024
+ output.printSuccess(`Removed ${key}`);
1025
+ return { success: true };
1026
+ }
1027
+ if (key) {
1028
+ const val = isLocal
1029
+ ? await browser.getLocalStorageKey(client, sessionId, key)
1030
+ : await browser.getSessionStorageKey(client, sessionId, key);
1031
+ if (ctx.flags.json)
1032
+ print(JSON.stringify({ data: val }));
1033
+ else
1034
+ print(val ?? '(null)');
1035
+ return { success: true, data: { value: val } };
1036
+ }
1037
+ const all = isLocal
1038
+ ? await browser.getAllLocalStorage(client, sessionId)
1039
+ : await browser.getAllSessionStorage(client, sessionId);
1040
+ if (ctx.flags.json)
1041
+ print(JSON.stringify(all));
1042
+ else {
1043
+ for (const [k, v] of Object.entries(all))
1044
+ print(` ${k}: ${v}`);
1045
+ }
1046
+ return { success: true, data: { storage: all } };
1047
+ },
1048
+ };
1049
+ const cookiesCommand = {
1050
+ name: 'cookies',
1051
+ description: 'Cookie management. Usage: monomind browse cookies [list|set|clear]',
1052
+ options: [
1053
+ { name: 'name', type: 'string', description: 'Cookie name' },
1054
+ { name: 'value', type: 'string', description: 'Cookie value' },
1055
+ { name: 'domain', type: 'string', description: 'Cookie domain' },
1056
+ { name: 'curl', type: 'string', description: 'Import cookies from cURL dump file' },
1057
+ ],
1058
+ action: async (ctx) => {
1059
+ const { client, sessionId } = await ensureConnected(_port);
1060
+ const browser = await getBrowser();
1061
+ const action = ctx.args[0] ?? 'list';
1062
+ switch (action) {
1063
+ case 'list': {
1064
+ const cookies = await browser.getCookies(client, sessionId);
1065
+ print(JSON.stringify(cookies, null, 2));
1066
+ return { success: true, data: { cookies } };
1067
+ }
1068
+ case 'set': {
1069
+ if (ctx.flags.name && ctx.flags.value) {
1070
+ await browser.setCookies(client, sessionId, [{
1071
+ name: ctx.flags.name,
1072
+ value: ctx.flags.value,
1073
+ domain: ctx.flags.domain,
1074
+ }]);
1075
+ output.printSuccess(`Cookie set: ${ctx.flags.name}`);
1076
+ }
1077
+ else {
1078
+ throw new Error('Usage: monomind browse cookies set --name <n> --value <v>');
1079
+ }
1080
+ break;
1081
+ }
1082
+ case 'clear':
1083
+ await browser.clearCookies(client, sessionId);
1084
+ output.printSuccess('Cookies cleared');
1085
+ break;
1086
+ default:
1087
+ throw new Error('Usage: monomind browse cookies list|set|clear');
1088
+ }
1089
+ return { success: true };
1090
+ },
1091
+ };
1092
+ const pdfCommand = {
1093
+ name: 'pdf',
1094
+ description: 'Save page as PDF. Usage: monomind browse pdf [path]',
1095
+ options: [
1096
+ { name: 'landscape', type: 'boolean', description: 'Landscape orientation', default: false },
1097
+ { name: 'background', type: 'boolean', description: 'Print background', default: true },
1098
+ ],
1099
+ action: async (ctx) => {
1100
+ const { client, sessionId } = await ensureConnected(_port);
1101
+ const browser = await getBrowser();
1102
+ const path = await browser.capturePdf(client, sessionId, {
1103
+ path: ctx.args[0],
1104
+ landscape: ctx.flags.landscape,
1105
+ printBackground: ctx.flags.background,
1106
+ });
1107
+ output.printSuccess(`PDF saved: ${path}`);
1108
+ return { success: true, data: { path } };
1109
+ },
1110
+ };
1111
+ const isCommand = {
1112
+ name: 'is',
1113
+ description: 'Check element state. Usage: monomind browse is visible|enabled|checked @e1',
1114
+ options: [
1115
+ { name: 'json', type: 'boolean', description: 'Output as JSON', default: false },
1116
+ ],
1117
+ action: async (ctx) => {
1118
+ const { client, sessionId } = await ensureConnected(_port);
1119
+ const browser = await getBrowser();
1120
+ const check = ctx.args[0];
1121
+ const refArg = ctx.args[1];
1122
+ if (!check || !refArg)
1123
+ throw new Error('Usage: monomind browse is visible|enabled|checked @e1');
1124
+ const refKey = refArg.startsWith('@') ? refArg.slice(1) : refArg;
1125
+ const ref = await browser.resolveRef(client, sessionId, _refs, refKey);
1126
+ let result;
1127
+ switch (check) {
1128
+ case 'visible':
1129
+ result = await browser.isVisible(client, sessionId, ref);
1130
+ break;
1131
+ case 'enabled':
1132
+ result = await browser.isEnabled(client, sessionId, ref);
1133
+ break;
1134
+ case 'checked':
1135
+ result = await browser.isChecked(client, sessionId, ref);
1136
+ break;
1137
+ default: throw new Error(`Unknown check: ${check}. Use: visible|enabled|checked`);
1138
+ }
1139
+ if (ctx.flags.json) {
1140
+ print(JSON.stringify({ data: { [check]: result } }));
1141
+ }
1142
+ else {
1143
+ print(result ? 'true' : 'false');
1144
+ }
1145
+ return { success: true, data: { [check]: result } };
1146
+ },
1147
+ };
1148
+ const findCommand = {
1149
+ name: 'find',
1150
+ description: 'Find elements by semantic locators. Usage: monomind browse find role|text|label|testid <value> [action]',
1151
+ options: [
1152
+ { name: 'name', type: 'string', description: 'Filter by accessible name' },
1153
+ { name: 'exact', type: 'boolean', description: 'Require exact match', default: false },
1154
+ { name: 'nth', type: 'number', description: 'Find nth match' },
1155
+ { name: 'last', type: 'boolean', description: 'Find last match', default: false },
1156
+ ],
1157
+ action: async (ctx) => {
1158
+ const { client, sessionId } = await ensureConnected(_port);
1159
+ const browser = await getBrowser();
1160
+ const locator = ctx.args[0];
1161
+ const value = ctx.args[1];
1162
+ const action = ctx.args[2];
1163
+ if (!locator || !value)
1164
+ throw new Error('Usage: monomind browse find role|text|label|testid <value> [action]');
1165
+ const opts = {
1166
+ name: ctx.flags.name,
1167
+ exact: ctx.flags.exact,
1168
+ nth: ctx.flags.nth,
1169
+ last: ctx.flags.last,
1170
+ };
1171
+ let ref = null;
1172
+ switch (locator) {
1173
+ case 'role':
1174
+ ref = await browser.findByRole(client, sessionId, _refs, value, opts);
1175
+ break;
1176
+ case 'text':
1177
+ ref = await browser.findByText(client, sessionId, _refs, value, opts);
1178
+ break;
1179
+ case 'label':
1180
+ ref = await browser.findByLabel(client, sessionId, _refs, value, opts);
1181
+ break;
1182
+ case 'testid': {
1183
+ const sel = await browser.findByTestId(client, sessionId, value);
1184
+ if (!sel) {
1185
+ output.printWarning(`testid not found: ${value}`);
1186
+ return { success: false };
1187
+ }
1188
+ output.printSuccess(`Found testid selector: ${sel}`);
1189
+ return { success: true, data: { selector: sel } };
1190
+ }
1191
+ default:
1192
+ throw new Error(`Unknown locator: ${locator}. Use: role|text|label|testid`);
1193
+ }
1194
+ if (!ref) {
1195
+ output.printWarning(`No element found: ${locator}="${value}"`);
1196
+ return { success: false };
1197
+ }
1198
+ output.printSuccess(`Found: ${ref.role} "${ref.name}" [@${ref.ref}]`);
1199
+ if (action) {
1200
+ switch (action) {
1201
+ case 'click':
1202
+ await browser.clickElement(client, sessionId, ref);
1203
+ break;
1204
+ case 'fill': {
1205
+ const fillValue = ctx.args[3];
1206
+ await browser.fillElement(client, sessionId, ref, fillValue ?? '');
1207
+ break;
1208
+ }
1209
+ case 'type': {
1210
+ const typeValue = ctx.args[3];
1211
+ await browser.typeIntoElement(client, sessionId, ref, typeValue ?? '');
1212
+ break;
1213
+ }
1214
+ case 'hover':
1215
+ await browser.hoverElement(client, sessionId, ref);
1216
+ break;
1217
+ case 'focus':
1218
+ await browser.focusElement(client, sessionId, ref);
1219
+ break;
1220
+ case 'check':
1221
+ await browser.checkElement(client, sessionId, ref, true);
1222
+ break;
1223
+ case 'uncheck':
1224
+ await browser.checkElement(client, sessionId, ref, false);
1225
+ break;
1226
+ case 'text': {
1227
+ const objectId = await browser.getObjectIdForRef(client, sessionId, ref);
1228
+ if (objectId) {
1229
+ const r = await client.send('Runtime.callFunctionOn', {
1230
+ functionDeclaration: 'function() { return this.innerText || this.textContent || ""; }',
1231
+ objectId,
1232
+ returnByValue: true,
1233
+ }, sessionId);
1234
+ print(r.result?.value ?? '');
1235
+ }
1236
+ break;
1237
+ }
1238
+ }
1239
+ }
1240
+ return { success: true, data: { ref } };
1241
+ },
1242
+ };
1243
+ const highlightCommand = {
1244
+ name: 'highlight',
1245
+ description: 'Highlight an element for 2 seconds. Usage: monomind browse highlight @e1',
1246
+ action: async (ctx) => {
1247
+ const { client, sessionId } = await ensureConnected(_port);
1248
+ const browser = await getBrowser();
1249
+ const refArg = ctx.args[0];
1250
+ if (!refArg)
1251
+ throw new Error('Usage: monomind browse highlight @e1');
1252
+ const refKey = refArg.startsWith('@') ? refArg.slice(1) : refArg;
1253
+ const ref = await browser.resolveRef(client, sessionId, _refs, refKey);
1254
+ await browser.highlightElement(client, sessionId, ref);
1255
+ output.printSuccess(`Highlighted: ${ref.role} "${ref.name}"`);
1256
+ return { success: true };
1257
+ },
1258
+ };
1259
+ const pushstateCommand = {
1260
+ name: 'pushstate',
1261
+ description: 'SPA navigation via pushState. Usage: monomind browse pushstate /path',
1262
+ action: async (ctx) => {
1263
+ const { client, sessionId } = await ensureConnected(_port);
1264
+ const browser = await getBrowser();
1265
+ const url = ctx.args[0];
1266
+ if (!url)
1267
+ throw new Error('Usage: monomind browse pushstate <url>');
1268
+ await browser.pushState(client, sessionId, url);
1269
+ output.printSuccess(`pushState: ${url}`);
1270
+ return { success: true };
1271
+ },
1272
+ };
1273
+ const batchCommand = {
1274
+ name: 'batch',
1275
+ description: 'Execute multiple commands. Usage: monomind browse batch "open url" "snapshot -i" "click @e1"',
1276
+ options: [
1277
+ { name: 'bail', type: 'boolean', description: 'Stop on first error', default: false },
1278
+ { name: 'json', type: 'boolean', description: 'Input from JSON stdin', default: false },
1279
+ ],
1280
+ action: async (ctx) => {
1281
+ const commands = ctx.args;
1282
+ if (commands.length === 0)
1283
+ throw new Error('Usage: monomind browse batch "cmd1" "cmd2" ...');
1284
+ const results = [];
1285
+ for (const cmdStr of commands) {
1286
+ const parts = cmdStr.trim().split(/\s+/);
1287
+ const subName = parts[0];
1288
+ const subArgs = parts.slice(1);
1289
+ const subCmd = browseCommand.subcommands?.find((s) => s.name === subName);
1290
+ if (!subCmd?.action) {
1291
+ const err = `Unknown command: ${subName}`;
1292
+ results.push({ command: cmdStr, success: false, error: err });
1293
+ if (ctx.flags.bail)
1294
+ break;
1295
+ continue;
1296
+ }
1297
+ try {
1298
+ const parsedFlags = { _: [] };
1299
+ // Parse --flags from subArgs
1300
+ for (let i = 0; i < subArgs.length; i++) {
1301
+ if (subArgs[i].startsWith('--')) {
1302
+ const key = subArgs[i].slice(2);
1303
+ const next = subArgs[i + 1];
1304
+ if (next && !next.startsWith('--')) {
1305
+ parsedFlags[key] = next;
1306
+ i++;
1307
+ }
1308
+ else {
1309
+ parsedFlags[key] = true;
1310
+ }
1311
+ }
1312
+ }
1313
+ const fakeCtx = {
1314
+ args: subArgs.filter((a) => !a.startsWith('--')),
1315
+ flags: parsedFlags,
1316
+ cwd: ctx.cwd,
1317
+ interactive: false,
1318
+ };
1319
+ await subCmd.action(fakeCtx);
1320
+ results.push({ command: cmdStr, success: true });
1321
+ }
1322
+ catch (e) {
1323
+ const err = e instanceof Error ? e.message : String(e);
1324
+ results.push({ command: cmdStr, success: false, error: err });
1325
+ output.printWarning(`Batch error in "${cmdStr}": ${err}`);
1326
+ if (ctx.flags.bail)
1327
+ break;
1328
+ }
1329
+ }
1330
+ const failed = results.filter((r) => !r.success).length;
1331
+ output.printInfo(`Batch: ${results.length - failed}/${results.length} succeeded`);
1332
+ return { success: failed === 0, data: { results } };
1333
+ },
1334
+ };
1335
+ const addinitscriptCommand = {
1336
+ name: 'addinitscript',
1337
+ description: 'Add script to run before page navigation. Usage: monomind browse addinitscript "window.x=1"',
1338
+ action: async (ctx) => {
1339
+ const { client, sessionId } = await ensureConnected(_port);
1340
+ const browser = await getBrowser();
1341
+ const script = ctx.args[0];
1342
+ if (!script)
1343
+ throw new Error('Usage: monomind browse addinitscript "<js>"');
1344
+ const id = await browser.addInitScript(client, sessionId, script);
1345
+ output.printSuccess(`Init script added: ${id}`);
1346
+ return { success: true, data: { identifier: id } };
1347
+ },
1348
+ };
1349
+ const removeinitscriptCommand = {
1350
+ name: 'removeinitscript',
1351
+ description: 'Remove a previously added init script. Usage: monomind browse removeinitscript <id>',
1352
+ action: async (ctx) => {
1353
+ const { client, sessionId } = await ensureConnected(_port);
1354
+ const browser = await getBrowser();
1355
+ const id = ctx.args[0];
1356
+ if (!id)
1357
+ throw new Error('Usage: monomind browse removeinitscript <identifier>');
1358
+ await browser.removeInitScript(client, sessionId, id);
1359
+ output.printSuccess(`Init script removed: ${id}`);
1360
+ return { success: true };
1361
+ },
1362
+ };
1363
+ // ---------------------------------------------------------------------------
503
1364
  // Root browse command
504
1365
  // ---------------------------------------------------------------------------
505
1366
  const browseCommand = {
@@ -509,8 +1370,23 @@ const browseCommand = {
509
1370
  openCommand,
510
1371
  snapshotCommand,
511
1372
  clickCommand,
1373
+ dblclickCommand,
512
1374
  fillCommand,
1375
+ typeCommand,
513
1376
  pressCommand,
1377
+ keyboardCommand,
1378
+ keydownCommand,
1379
+ keyupCommand,
1380
+ hoverCommand,
1381
+ focusCommand,
1382
+ selectCommand,
1383
+ checkCommand,
1384
+ uncheckCommand,
1385
+ scrollIntoViewCommand,
1386
+ dragCommand,
1387
+ uploadCommand,
1388
+ mouseCommand,
1389
+ clipboardCommand,
514
1390
  waitCommand,
515
1391
  screenshotCommand,
516
1392
  getCommand,
@@ -520,6 +1396,21 @@ const browseCommand = {
520
1396
  stateCommand,
521
1397
  networkCommand,
522
1398
  evalCommand,
1399
+ dialogCommand,
1400
+ frameCommand,
1401
+ tabCommand,
1402
+ consoleLogCommand,
1403
+ errorsCommand,
1404
+ storageCommand,
1405
+ cookiesCommand,
1406
+ pdfCommand,
1407
+ isCommand,
1408
+ findCommand,
1409
+ highlightCommand,
1410
+ pushstateCommand,
1411
+ batchCommand,
1412
+ addinitscriptCommand,
1413
+ removeinitscriptCommand,
523
1414
  closeCommand,
524
1415
  ],
525
1416
  options: [
@@ -551,21 +1442,51 @@ const browseCommand = {
551
1442
  output.printInfo('Usage: monomind browse <subcommand> [options]');
552
1443
  output.printInfo('');
553
1444
  output.printInfo('Subcommands:');
554
- output.printInfo(' open Open a URL');
555
- output.printInfo(' snapshot Capture accessibility snapshot with refs');
556
- output.printInfo(' click Click an element by ref');
557
- output.printInfo(' fill Fill an input element');
558
- output.printInfo(' press Press a keyboard key');
559
- output.printInfo(' wait Wait for a condition');
560
- output.printInfo(' screenshot Take a screenshot');
561
- output.printInfo(' get Get page info (url, title, text, html)');
562
- output.printInfo(' scroll Scroll the page');
563
- output.printInfo(' navigate Navigate history (back/forward/reload)');
564
- output.printInfo(' set Configure viewport or user agent');
565
- output.printInfo(' state Save/load/list session state');
566
- output.printInfo(' network Network interception and cookies');
567
- output.printInfo(' eval Evaluate JavaScript');
568
- output.printInfo(' close Close the browser session');
1445
+ output.printInfo(' open Open a URL');
1446
+ output.printInfo(' snapshot Capture accessibility snapshot with refs');
1447
+ output.printInfo(' click Click an element by ref');
1448
+ output.printInfo(' dblclick Double-click an element');
1449
+ output.printInfo(' fill Fill an input (clears first)');
1450
+ output.printInfo(' type Type into element (appends)');
1451
+ output.printInfo(' press Press a keyboard key');
1452
+ output.printInfo(' keyboard Insert text directly');
1453
+ output.printInfo(' keydown Hold a key down');
1454
+ output.printInfo(' keyup Release a held key');
1455
+ output.printInfo(' hover Hover over element');
1456
+ output.printInfo(' focus Focus an element');
1457
+ output.printInfo(' select Select a dropdown option');
1458
+ output.printInfo(' check Check a checkbox');
1459
+ output.printInfo(' uncheck Uncheck a checkbox');
1460
+ output.printInfo(' scrollintoview Scroll element into view');
1461
+ output.printInfo(' drag Drag element to another element');
1462
+ output.printInfo(' upload Upload file(s) to file input');
1463
+ output.printInfo(' mouse Fine-grained mouse control');
1464
+ output.printInfo(' clipboard Read/write clipboard');
1465
+ output.printInfo(' wait Wait for a condition');
1466
+ output.printInfo(' screenshot Take a screenshot');
1467
+ output.printInfo(' get Get page info (url, title, text, html)');
1468
+ output.printInfo(' scroll Scroll the page');
1469
+ output.printInfo(' navigate Navigate history (back/forward/reload)');
1470
+ output.printInfo(' set Configure viewport, device, user agent');
1471
+ output.printInfo(' state Save/load/list session state');
1472
+ output.printInfo(' network Network interception and cookies');
1473
+ output.printInfo(' eval Evaluate JavaScript');
1474
+ output.printInfo(' dialog Handle browser dialogs');
1475
+ output.printInfo(' frame Switch to iframe');
1476
+ output.printInfo(' tab Tab management');
1477
+ output.printInfo(' console View captured console messages');
1478
+ output.printInfo(' errors View page JS errors');
1479
+ output.printInfo(' storage localStorage/sessionStorage management');
1480
+ output.printInfo(' cookies Cookie management');
1481
+ output.printInfo(' pdf Save page as PDF');
1482
+ output.printInfo(' is Check element state (visible/enabled/checked)');
1483
+ output.printInfo(' find Find elements by semantic locators');
1484
+ output.printInfo(' highlight Highlight an element visually');
1485
+ output.printInfo(' pushstate SPA navigation via pushState');
1486
+ output.printInfo(' batch Execute multiple commands');
1487
+ output.printInfo(' addinitscript Add script to run before page navigation');
1488
+ output.printInfo(' removeinitscript Remove a previously added init script');
1489
+ output.printInfo(' close Close the browser session');
569
1490
  return { success: true };
570
1491
  },
571
1492
  };