@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.
- package/.claude/helpers/handlers/adr-draft-handler.cjs +64 -0
- package/.claude/helpers/handlers/agent-start-handler.cjs +99 -0
- package/.claude/helpers/handlers/graph-status-handler.cjs +38 -0
- package/.claude/helpers/handlers/session-restore-handler.cjs +45 -40
- package/.claude/helpers/hook-handler.cjs +6 -158
- package/dist/src/browser/actions.d.ts +15 -0
- package/dist/src/browser/actions.d.ts.map +1 -1
- package/dist/src/browser/actions.js +91 -0
- package/dist/src/browser/actions.js.map +1 -1
- package/dist/src/browser/batch.d.ts +13 -0
- package/dist/src/browser/batch.d.ts.map +1 -0
- package/dist/src/browser/batch.js +11 -0
- package/dist/src/browser/batch.js.map +1 -0
- package/dist/src/browser/console-log.d.ts +22 -0
- package/dist/src/browser/console-log.d.ts.map +1 -0
- package/dist/src/browser/console-log.js +55 -0
- package/dist/src/browser/console-log.js.map +1 -0
- package/dist/src/browser/dialog.d.ts +11 -0
- package/dist/src/browser/dialog.d.ts.map +1 -0
- package/dist/src/browser/dialog.js +36 -0
- package/dist/src/browser/dialog.js.map +1 -0
- package/dist/src/browser/emulation.d.ts +15 -0
- package/dist/src/browser/emulation.d.ts.map +1 -0
- package/dist/src/browser/emulation.js +62 -0
- package/dist/src/browser/emulation.js.map +1 -0
- package/dist/src/browser/find.d.ts +21 -0
- package/dist/src/browser/find.d.ts.map +1 -0
- package/dist/src/browser/find.js +118 -0
- package/dist/src/browser/find.js.map +1 -0
- package/dist/src/browser/index.d.ts +7 -0
- package/dist/src/browser/index.d.ts.map +1 -1
- package/dist/src/browser/index.js +7 -0
- package/dist/src/browser/index.js.map +1 -1
- package/dist/src/browser/pdf.d.ts +15 -0
- package/dist/src/browser/pdf.d.ts.map +1 -0
- package/dist/src/browser/pdf.js +27 -0
- package/dist/src/browser/pdf.js.map +1 -0
- package/dist/src/browser/storage.d.ts +11 -0
- package/dist/src/browser/storage.d.ts.map +1 -0
- package/dist/src/browser/storage.js +43 -0
- package/dist/src/browser/storage.js.map +1 -0
- package/dist/src/commands/browse.d.ts.map +1 -1
- package/dist/src/commands/browse.js +939 -18
- package/dist/src/commands/browse.js.map +1 -1
- package/dist/src/ui/dashboard-v2.html +182 -17
- package/dist/src/ui/server.mjs +56 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- 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
|
|
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 <
|
|
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
|
|
555
|
-
output.printInfo(' snapshot
|
|
556
|
-
output.printInfo(' click
|
|
557
|
-
output.printInfo('
|
|
558
|
-
output.printInfo('
|
|
559
|
-
output.printInfo('
|
|
560
|
-
output.printInfo('
|
|
561
|
-
output.printInfo('
|
|
562
|
-
output.printInfo('
|
|
563
|
-
output.printInfo('
|
|
564
|
-
output.printInfo('
|
|
565
|
-
output.printInfo('
|
|
566
|
-
output.printInfo('
|
|
567
|
-
output.printInfo('
|
|
568
|
-
output.printInfo('
|
|
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
|
};
|