browser-pilot-cli 0.1.4 → 0.1.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +12 -1
- package/dist/cli.js +47 -11
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -96,8 +96,10 @@ The daemon maintains a single CDP WebSocket connection. A pulsing blue glow arou
|
|
|
96
96
|
|---------|---------|-------------|
|
|
97
97
|
| `bp open <url>` | snapshot | Navigate to URL |
|
|
98
98
|
| `bp snapshot` | snapshot | Get interactive elements |
|
|
99
|
-
| `bp click <ref>` | snapshot | Click element by ref number |
|
|
99
|
+
| `bp click <ref>` | snapshot | Click element by ref number (`--double`, `--right`) |
|
|
100
|
+
| `bp click 0 --xy x,y` | snapshot | Click at viewport coordinates (canvas, maps) |
|
|
100
101
|
| `bp type <ref> <text>` | snapshot | Type into element (`--clear`, `--submit`) |
|
|
102
|
+
| `bp keyboard <text>` | snapshot | Type via keyboard events (`--click`, `--clear`) |
|
|
101
103
|
| `bp press <key>` | snapshot | Press key (Enter, Escape, Control+a, Meta+c) |
|
|
102
104
|
| `bp eval [js]` | value | Run JavaScript (escape hatch for anything) |
|
|
103
105
|
|
|
@@ -211,6 +213,15 @@ bp upload ~/Downloads/photo.jpg # auto-finds file input, triggers upload
|
|
|
211
213
|
bp type 3 "new content" --clear # replace content in a rich text editor
|
|
212
214
|
```
|
|
213
215
|
|
|
216
|
+
For canvas-based editors (Google Docs, Google Sheets, Figma), use `bp keyboard` which sends real keyboard events:
|
|
217
|
+
|
|
218
|
+
```bash
|
|
219
|
+
bp keyboard "Hello Docs!" --click ".kix-appview-editor" # Google Docs
|
|
220
|
+
bp press Meta+b # toggle bold
|
|
221
|
+
bp keyboard "bold text"
|
|
222
|
+
bp click 0 --xy 400,300 # click canvas area
|
|
223
|
+
```
|
|
224
|
+
|
|
214
225
|
Shadow DOM elements are traversed automatically — no special commands needed. Elements inside open shadow roots (even deeply nested) appear in snapshots and can be clicked/typed normally.
|
|
215
226
|
|
|
216
227
|
For `<select>` dropdowns (shown as `combobox`), use `bp eval`:
|
package/dist/cli.js
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
import { Command } from "commander";
|
|
5
5
|
import { writeFileSync as writeFileSync3, readFileSync as readFileSync5, existsSync as existsSync6 } from "fs";
|
|
6
6
|
import { resolve as resolvePath } from "path";
|
|
7
|
+
import { createRequire } from "module";
|
|
7
8
|
|
|
8
9
|
// src/session.ts
|
|
9
10
|
import { spawn } from "child_process";
|
|
@@ -542,8 +543,10 @@ async function resolveTarget(transport, sessionId, target, targetId) {
|
|
|
542
543
|
}
|
|
543
544
|
|
|
544
545
|
// src/cli.ts
|
|
546
|
+
var require2 = createRequire(import.meta.url);
|
|
547
|
+
var PKG_VERSION = require2("../package.json").version;
|
|
545
548
|
var program = new Command();
|
|
546
|
-
program.name("bp").description("Control your browser from the command line").version(
|
|
549
|
+
program.name("bp").description("Control your browser from the command line").version(PKG_VERSION).option("--human", "force human-readable output (default when TTY)").addHelpText("after", `
|
|
547
550
|
Workflow:
|
|
548
551
|
bp connect # one-time setup (click Allow in Chrome)
|
|
549
552
|
bp open <url> # navigate \u2014 returns snapshot with [ref] numbers
|
|
@@ -659,7 +662,30 @@ var KEY_DEFS = {
|
|
|
659
662
|
home: { key: "Home", code: "Home", keyCode: 36 },
|
|
660
663
|
end: { key: "End", code: "End", keyCode: 35 },
|
|
661
664
|
pageup: { key: "PageUp", code: "PageUp", keyCode: 33 },
|
|
662
|
-
pagedown: { key: "PageDown", code: "PageDown", keyCode: 34 }
|
|
665
|
+
pagedown: { key: "PageDown", code: "PageDown", keyCode: 34 },
|
|
666
|
+
// Digits
|
|
667
|
+
"0": { key: "0", code: "Digit0", keyCode: 48 },
|
|
668
|
+
"1": { key: "1", code: "Digit1", keyCode: 49 },
|
|
669
|
+
"2": { key: "2", code: "Digit2", keyCode: 50 },
|
|
670
|
+
"3": { key: "3", code: "Digit3", keyCode: 51 },
|
|
671
|
+
"4": { key: "4", code: "Digit4", keyCode: 52 },
|
|
672
|
+
"5": { key: "5", code: "Digit5", keyCode: 53 },
|
|
673
|
+
"6": { key: "6", code: "Digit6", keyCode: 54 },
|
|
674
|
+
"7": { key: "7", code: "Digit7", keyCode: 55 },
|
|
675
|
+
"8": { key: "8", code: "Digit8", keyCode: 56 },
|
|
676
|
+
"9": { key: "9", code: "Digit9", keyCode: 57 },
|
|
677
|
+
// Punctuation
|
|
678
|
+
"-": { key: "-", code: "Minus", keyCode: 189 },
|
|
679
|
+
"=": { key: "=", code: "Equal", keyCode: 187 },
|
|
680
|
+
"[": { key: "[", code: "BracketLeft", keyCode: 219 },
|
|
681
|
+
"]": { key: "]", code: "BracketRight", keyCode: 221 },
|
|
682
|
+
"\\": { key: "\\", code: "Backslash", keyCode: 220 },
|
|
683
|
+
";": { key: ";", code: "Semicolon", keyCode: 186 },
|
|
684
|
+
"'": { key: "'", code: "Quote", keyCode: 222 },
|
|
685
|
+
",": { key: ",", code: "Comma", keyCode: 188 },
|
|
686
|
+
".": { key: ".", code: "Period", keyCode: 190 },
|
|
687
|
+
"/": { key: "/", code: "Slash", keyCode: 191 },
|
|
688
|
+
"`": { key: "`", code: "Backquote", keyCode: 192 }
|
|
663
689
|
};
|
|
664
690
|
var MOD_DEFS = {
|
|
665
691
|
control: { key: "Control", code: "ControlLeft", keyCode: 17, mask: 2 },
|
|
@@ -761,6 +787,7 @@ Examples:
|
|
|
761
787
|
bp click 0 --xy 400,300 # click at coordinates (ref ignored)
|
|
762
788
|
bp click 0 --xy 400,300 --double # double-click at coordinates
|
|
763
789
|
bp click 0 --xy 400,300 --right # right-click at coordinates`).action(action(async (ref, opts) => {
|
|
790
|
+
if (opts.double && opts.right) throw new Error("--double and --right are mutually exclusive");
|
|
764
791
|
const limit = parseLimit(opts.limit);
|
|
765
792
|
await withPilot(async ({ transport, sessionId, state }) => {
|
|
766
793
|
if (opts.xy) {
|
|
@@ -848,6 +875,13 @@ program.command("type <ref> <text>").description("Type text into element and ret
|
|
|
848
875
|
emitSnapshot(await snap(transport, sessionId, state.activeTargetId, limit));
|
|
849
876
|
});
|
|
850
877
|
}));
|
|
878
|
+
function charToCode(char) {
|
|
879
|
+
const kd = KEY_DEFS[char];
|
|
880
|
+
if (kd) return kd;
|
|
881
|
+
if (/^[a-zA-Z]$/.test(char)) return { key: char, code: `Key${char.toUpperCase()}`, keyCode: char.toUpperCase().charCodeAt(0) };
|
|
882
|
+
if (char === " ") return { key: " ", code: "Space", keyCode: 32 };
|
|
883
|
+
return { key: char, code: "", keyCode: char.charCodeAt(0) };
|
|
884
|
+
}
|
|
851
885
|
async function typeViaKeyboard(t, sid, text) {
|
|
852
886
|
for (const char of text) {
|
|
853
887
|
if (char === "\n") {
|
|
@@ -855,10 +889,9 @@ async function typeViaKeyboard(t, sid, text) {
|
|
|
855
889
|
} else if (char === " ") {
|
|
856
890
|
await dispatchKey(t, sid, "Tab");
|
|
857
891
|
} else if (char.charCodeAt(0) >= 32 && char.charCodeAt(0) <= 126) {
|
|
858
|
-
const
|
|
859
|
-
|
|
860
|
-
await t.send("Input.dispatchKeyEvent", { type: "
|
|
861
|
-
await t.send("Input.dispatchKeyEvent", { type: "keyUp", key: char, code, windowsVirtualKeyCode: keyCode }, sid);
|
|
892
|
+
const { key, code, keyCode } = charToCode(char);
|
|
893
|
+
await t.send("Input.dispatchKeyEvent", { type: "keyDown", key, code, windowsVirtualKeyCode: keyCode, text: char }, sid);
|
|
894
|
+
await t.send("Input.dispatchKeyEvent", { type: "keyUp", key, code, windowsVirtualKeyCode: keyCode }, sid);
|
|
862
895
|
} else {
|
|
863
896
|
await t.send("Input.insertText", { text: char }, sid);
|
|
864
897
|
}
|
|
@@ -890,19 +923,22 @@ Examples:
|
|
|
890
923
|
await new Promise((r) => setTimeout(r, 300));
|
|
891
924
|
}
|
|
892
925
|
if (opts.clear) {
|
|
893
|
-
|
|
926
|
+
const selectAllMod = process.platform === "darwin" ? "Meta" : "Control";
|
|
927
|
+
await dispatchKey(transport, sessionId, `${selectAllMod}+a`);
|
|
894
928
|
await dispatchKey(transport, sessionId, "Delete");
|
|
895
929
|
}
|
|
896
930
|
if (opts.delay) {
|
|
897
931
|
const delay = parseInt(opts.delay, 10);
|
|
932
|
+
if (isNaN(delay) || delay < 0) throw new Error("--delay must be a non-negative number");
|
|
898
933
|
for (const char of text) {
|
|
899
934
|
if (char === "\n") {
|
|
900
935
|
await dispatchKey(transport, sessionId, "Enter");
|
|
936
|
+
} else if (char === " ") {
|
|
937
|
+
await dispatchKey(transport, sessionId, "Tab");
|
|
901
938
|
} else if (char.charCodeAt(0) >= 32 && char.charCodeAt(0) <= 126) {
|
|
902
|
-
const
|
|
903
|
-
|
|
904
|
-
await transport.send("Input.dispatchKeyEvent", { type: "
|
|
905
|
-
await transport.send("Input.dispatchKeyEvent", { type: "keyUp", key: char, code, windowsVirtualKeyCode: keyCode }, sessionId);
|
|
939
|
+
const { key, code, keyCode } = charToCode(char);
|
|
940
|
+
await transport.send("Input.dispatchKeyEvent", { type: "keyDown", key, code, windowsVirtualKeyCode: keyCode, text: char }, sessionId);
|
|
941
|
+
await transport.send("Input.dispatchKeyEvent", { type: "keyUp", key, code, windowsVirtualKeyCode: keyCode }, sessionId);
|
|
906
942
|
} else {
|
|
907
943
|
await transport.send("Input.insertText", { text: char }, sessionId);
|
|
908
944
|
}
|