fastbrowser_cli 1.0.35 → 1.0.39

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 (102) hide show
  1. package/README.md +26 -5
  2. package/dist/contribs/_shared/fastbrowser_helper.d.ts +13 -0
  3. package/dist/contribs/_shared/fastbrowser_helper.d.ts.map +1 -0
  4. package/dist/contribs/_shared/fastbrowser_helper.js +39 -0
  5. package/dist/contribs/_shared/fastbrowser_helper.js.map +1 -0
  6. package/dist/contribs/linkedin_cli/src/cli.d.ts +3 -0
  7. package/dist/contribs/linkedin_cli/src/cli.d.ts.map +1 -0
  8. package/dist/contribs/linkedin_cli/src/cli.js +299 -0
  9. package/dist/contribs/linkedin_cli/src/cli.js.map +1 -0
  10. package/dist/contribs/linkedin_cli/src/libs/linkedin_profile_helper.d.ts +73 -0
  11. package/dist/contribs/linkedin_cli/src/libs/linkedin_profile_helper.d.ts.map +1 -0
  12. package/dist/contribs/linkedin_cli/src/libs/linkedin_profile_helper.js +866 -0
  13. package/dist/contribs/linkedin_cli/src/libs/linkedin_profile_helper.js.map +1 -0
  14. package/dist/contribs/linkedin_cli/src/libs/linkedin_recent_posts_helper.d.ts +61 -0
  15. package/dist/contribs/linkedin_cli/src/libs/linkedin_recent_posts_helper.d.ts.map +1 -0
  16. package/dist/contribs/linkedin_cli/src/libs/linkedin_recent_posts_helper.js +885 -0
  17. package/dist/contribs/linkedin_cli/src/libs/linkedin_recent_posts_helper.js.map +1 -0
  18. package/dist/contribs/linkedin_cli/src/libs/linkedin_thread_helper.d.ts +11 -0
  19. package/dist/contribs/linkedin_cli/src/libs/linkedin_thread_helper.d.ts.map +1 -0
  20. package/dist/contribs/linkedin_cli/src/libs/linkedin_thread_helper.js +145 -0
  21. package/dist/contribs/linkedin_cli/src/libs/linkedin_thread_helper.js.map +1 -0
  22. package/dist/contribs/twitter_cli/src/cli.d.ts +3 -0
  23. package/dist/contribs/twitter_cli/src/cli.d.ts.map +1 -0
  24. package/dist/contribs/twitter_cli/src/cli.js +273 -0
  25. package/dist/contribs/twitter_cli/src/cli.js.map +1 -0
  26. package/dist/contribs/twitter_cli/src/libs/twitter_profile_helper.d.ts +28 -0
  27. package/dist/contribs/twitter_cli/src/libs/twitter_profile_helper.d.ts.map +1 -0
  28. package/dist/contribs/twitter_cli/src/libs/twitter_profile_helper.js +274 -0
  29. package/dist/contribs/twitter_cli/src/libs/twitter_profile_helper.js.map +1 -0
  30. package/dist/contribs/twitter_cli/src/libs/twitter_recent_posts_helper.d.ts +43 -0
  31. package/dist/contribs/twitter_cli/src/libs/twitter_recent_posts_helper.d.ts.map +1 -0
  32. package/dist/contribs/twitter_cli/src/libs/twitter_recent_posts_helper.js +519 -0
  33. package/dist/contribs/twitter_cli/src/libs/twitter_recent_posts_helper.js.map +1 -0
  34. package/dist/contribs/twitter_cli/src/libs/twitter_thread_helper.d.ts +11 -0
  35. package/dist/contribs/twitter_cli/src/libs/twitter_thread_helper.d.ts.map +1 -0
  36. package/dist/contribs/twitter_cli/src/libs/twitter_thread_helper.js +213 -0
  37. package/dist/contribs/twitter_cli/src/libs/twitter_thread_helper.js.map +1 -0
  38. package/dist/fastbrowser_cli/fastbrowser_cli.js +43 -0
  39. package/dist/fastbrowser_cli/fastbrowser_cli.js.map +1 -1
  40. package/dist/fastbrowser_httpd/libs/tool-schemas.d.ts +4 -0
  41. package/dist/fastbrowser_httpd/libs/tool-schemas.d.ts.map +1 -1
  42. package/dist/fastbrowser_httpd/libs/tool-schemas.js +4 -0
  43. package/dist/fastbrowser_httpd/libs/tool-schemas.js.map +1 -1
  44. package/dist/fastbrowser_mcp/fastbrowser_mcp.js +36 -2
  45. package/dist/fastbrowser_mcp/fastbrowser_mcp.js.map +1 -1
  46. package/dist/fastbrowser_mcp/libs/mcp_target_helper.d.ts +2 -0
  47. package/dist/fastbrowser_mcp/libs/mcp_target_helper.d.ts.map +1 -1
  48. package/dist/fastbrowser_mcp/libs/mcp_target_helper.js +12 -0
  49. package/dist/fastbrowser_mcp/libs/mcp_target_helper.js.map +1 -1
  50. package/dist/fastbrowser_mcp/libs/response_formatter.d.ts +1 -0
  51. package/dist/fastbrowser_mcp/libs/response_formatter.d.ts.map +1 -1
  52. package/dist/fastbrowser_mcp/libs/response_formatter.js +27 -0
  53. package/dist/fastbrowser_mcp/libs/response_formatter.js.map +1 -1
  54. package/dist/shared/fastbrowser_helper.d.ts +13 -0
  55. package/dist/shared/fastbrowser_helper.d.ts.map +1 -0
  56. package/dist/shared/fastbrowser_helper.js +39 -0
  57. package/dist/shared/fastbrowser_helper.js.map +1 -0
  58. package/examples/linkedin_cli_TOREMOVE/README.md +7 -0
  59. package/examples/linkedin_cli_TOREMOVE/linkedin_dm.sh +40 -0
  60. package/examples/linkedin_cli_TOREMOVE/linkedin_dm.ts +326 -0
  61. package/examples/linkedin_cli_TOREMOVE/linkedin_dm_messages.ts +279 -0
  62. package/examples/linkedin_cli_TOREMOVE/linkedin_full_cycle.sh +5 -0
  63. package/examples/{linkedin_cli/linked_post.sh → linkedin_cli_TOREMOVE/linkedin_post.sh} +3 -0
  64. package/examples/linkedin_cli_TOREMOVE/message_thread.a11y.txt +252 -0
  65. package/examples/whatsapp/whatapp.a11y.txt +1521 -0
  66. package/examples/whatsapp/whatsapp.sh +10 -0
  67. package/listitem +7 -0
  68. package/package.json +7 -3
  69. package/skills/fastbrowser/SKILL.md +116 -29
  70. package/src/contribs/_shared/fastbrowser_helper.ts +49 -0
  71. package/src/contribs/linkedin_cli/README.md +80 -0
  72. package/src/contribs/linkedin_cli/data/linkedin_posts_jeromeetienne.a11y.txt +2364 -0
  73. package/src/contribs/linkedin_cli/data/linkedin_posts_jontwigge.a11y.txt +2740 -0
  74. package/src/contribs/linkedin_cli/data/linkedin_posts_julien_guezennec.a11y.txt +2073 -0
  75. package/src/contribs/linkedin_cli/data/linkedin_profile_jeromeetienne.a11y.txt +1863 -0
  76. package/src/contribs/linkedin_cli/data/linkedin_profile_jontwigge.a11y.txt +1738 -0
  77. package/src/contribs/linkedin_cli/data/linkedin_profile_julien_guezennec.a11y.txt +2182 -0
  78. package/src/contribs/linkedin_cli/src/cli.ts +345 -0
  79. package/src/contribs/linkedin_cli/src/libs/linkedin_profile_helper.ts +964 -0
  80. package/src/contribs/linkedin_cli/src/libs/linkedin_recent_posts_helper.ts +982 -0
  81. package/src/contribs/linkedin_cli/src/libs/linkedin_thread_helper.ts +171 -0
  82. package/src/contribs/twitter_cli/README.md +79 -0
  83. package/src/contribs/twitter_cli/data/twitter_chat.a11y.txt +215 -0
  84. package/src/contribs/twitter_cli/data/twitter_home.a11y.txt +467 -0
  85. package/src/contribs/twitter_cli/data/twitter_profile.a11y.txt +418 -0
  86. package/src/contribs/twitter_cli/data/twitter_profile_jontwigge.a11y.txt +484 -0
  87. package/src/contribs/twitter_cli/data/twitter_profile_molokoloco.a11y.txt +483 -0
  88. package/src/contribs/twitter_cli/src/cli.ts +315 -0
  89. package/src/contribs/twitter_cli/src/libs/twitter_profile_helper.ts +328 -0
  90. package/src/contribs/twitter_cli/src/libs/twitter_recent_posts_helper.ts +607 -0
  91. package/src/contribs/twitter_cli/src/libs/twitter_thread_helper.ts +240 -0
  92. package/src/fastbrowser_cli/fastbrowser_cli.ts +51 -0
  93. package/src/fastbrowser_httpd/libs/tool-schemas.ts +6 -0
  94. package/src/fastbrowser_mcp/fastbrowser_mcp.ts +46 -3
  95. package/src/fastbrowser_mcp/libs/mcp_target_helper.ts +11 -0
  96. package/src/fastbrowser_mcp/libs/response_formatter.ts +29 -0
  97. package/src/shared/fastbrowser_helper.ts +49 -0
  98. package/tsconfig.json +1 -1
  99. package/examples/linkedin_cli/linked_dm.sh +0 -19
  100. package/examples/mcp_client_playwright.ts +0 -34
  101. /package/examples/{linkedin_cli → linkedin_cli_TOREMOVE}/linkedin.snapshot.txt +0 -0
  102. /package/examples/{twitter_cli → twitter_cli_TOREMOVE}/twitter_post.sh +0 -0
@@ -0,0 +1,10 @@
1
+ #!/bin/env bash
2
+
3
+ # restart the server
4
+ NODE_OPTIONS='' NPM_CONFIG_LOGLEVEL=silent npm run dev:cli -- server restart
5
+
6
+ # navigate to whatsapp web
7
+ NODE_OPTIONS='' NPM_CONFIG_LOGLEVEL=silent npm run dev:cli -- navigate_page --url https://web.whatsapp.com/
8
+
9
+ # List all conversations in the chat list
10
+ NODE_OPTIONS='' NPM_CONFIG_LOGLEVEL=silent npm run dev:cli -- query_selectors -s 'grid[name="Chat list"] > row' -a
package/listitem ADDED
@@ -0,0 +1,7 @@
1
+
2
+ > fastbrowser_cli@1.0.37 dev:cli
3
+ > npx tsx ./src/fastbrowser_cli/fastbrowser_cli.ts click -s list[name=Conversation List] heading[name^=Eric Defiez]
4
+
5
+ Expected ] at column 22:
6
+ list[name=Conversation List]
7
+ ^
package/package.json CHANGED
@@ -1,11 +1,13 @@
1
1
  {
2
2
  "name": "fastbrowser_cli",
3
- "version": "1.0.35",
3
+ "version": "1.0.39",
4
4
  "description": "A CLI tool for FastBrowser, providing commands to interact with the FastBrowser MCP (Model Context Protocol) server and perform various browser automation tasks.",
5
5
  "main": "dist/fastbrowser_cli/fastbrowser_cli.js",
6
6
  "bin": {
7
7
  "fastbrowser_cli": "./dist/fastbrowser_cli/fastbrowser_cli.js",
8
- "fastbrowser_mcp": "./dist/fastbrowser_mcp/fastbrowser_mcp.js"
8
+ "fastbrowser_mcp": "./dist/fastbrowser_mcp/fastbrowser_mcp.js",
9
+ "linkedin_cli": "./dist/contribs/linkedin_cli/src/cli.js",
10
+ "twitter_cli": "./dist/contribs/twitter_cli/src/cli.js"
9
11
  },
10
12
  "keywords": [],
11
13
  "author": "",
@@ -30,7 +32,7 @@
30
32
  "typescript": "^6.0.3",
31
33
  "zod": "^4.3.6",
32
34
  "zod-from-json-schema": "^0.5.2",
33
- "a11y_parse": "^1.0.5"
35
+ "a11y_parse": "^1.0.8"
34
36
  },
35
37
  "devDependencies": {
36
38
  "@types/express": "^4.17.21",
@@ -47,6 +49,8 @@
47
49
  "inspect:playwright:chrome": "npx @modelcontextprotocol/inspector npx @playwright/mcp@latest --cdp-endpoint=chrome",
48
50
  "inspect:playwright:cdp_endpoint": "npx @modelcontextprotocol/inspector npx @playwright/mcp@latest --cdp-endpoint=http://localhost:9222",
49
51
  "inspect:playwright:extension": "npx @modelcontextprotocol/inspector npx @playwright/mcp@latest --extension",
52
+ "contribs:linkedin:profile": "npx tsx ./src/contribs/linkedin_cli/src/cli.ts profile jeromeetienne",
53
+ "contribs:twitter:profile": "npx tsx ./src/contribs/twitter_cli/src/cli.ts profile jerome_etienne",
50
54
  "build": "tsc -p tsconfig.build.json",
51
55
  "prepublish:check": "test -z \"$(git status --porcelain)\" || (echo 'Working tree dirty — commit or stash first' && exit 1)",
52
56
  "version:bump": "npm version patch --no-git-tag-version && git add package.json && git commit -m \"feat: bump version in fastbrowser_cli package.json\"",
@@ -15,7 +15,7 @@ maps 1-to-1 to a FastBrowser tool and returns the tool's response on stdout.
15
15
  Run the CLI directly via `tsx`:
16
16
 
17
17
  ```bash
18
- npx fastbrowser_cli <command> [flags]
18
+ npx fastbrowser_cli@latest <command> [flags]
19
19
  ```
20
20
 
21
21
  ## Typical Workflow
@@ -35,16 +35,41 @@ uid=1_0 RootWebArea "Example Domain" url="https://example.com/"
35
35
 
36
36
  ```bash
37
37
  # List all open browser pages
38
- npx fastbrowser_cli list_pages
38
+ npx fastbrowser_cli@latest list_pages
39
39
 
40
40
  # Open a new page at a URL
41
- npx fastbrowser_cli new_page --url https://example.com
41
+ npx fastbrowser_cli@latest new_page --url https://example.com
42
42
 
43
43
  # Close a page by its numeric id
44
- npx fastbrowser_cli close_page --page-id 1
44
+ npx fastbrowser_cli@latest close_page --page-id 1
45
45
 
46
46
  # Navigate the current page to a URL
47
- npx fastbrowser_cli navigate_page --url https://example.com
47
+ npx fastbrowser_cli@latest navigate_page --url https://example.com
48
+
49
+ # Restart the daemon - run this if pages opened by the bridge were closed manually
50
+ # and the MCP connection has broken
51
+ npx fastbrowser_cli@latest server restart
52
+ ```
53
+
54
+
55
+ ## Configuration
56
+
57
+ Global flags accepted by every command:
58
+
59
+ | Flag / env | Purpose | Default |
60
+ |---|---|---|
61
+ | `--server <url>` / `FASTBROWSER_SERVER` | URL of the `fastbrowser_httpd` daemon | `http://localhost:8787` |
62
+ | `--autostart` / `--no-autostart` | Auto-start the daemon if it is not already running | `--autostart` |
63
+ | `--mcp-target <target>` / `FASTBROWSER_MCP_TARGET` | Browser backend: `playwright` or `chrome_devtools` | `playwright` |
64
+
65
+ The daemon binds to one backend at startup. If it is already running with a different backend, the CLI refuses the request and prints the exact restart command to switch.
66
+
67
+ ```bash
68
+ # Use chrome-devtools-mcp for one command
69
+ npx fastbrowser_cli@latest --mcp-target chrome_devtools list_pages
70
+
71
+ # Switch the running daemon to a different backend
72
+ npx fastbrowser_cli@latest --mcp-target chrome_devtools server restart
48
73
  ```
49
74
 
50
75
 
@@ -59,7 +84,7 @@ Matches nodes by their accessibility role.
59
84
  ```
60
85
  button
61
86
  link
62
- comboxbox
87
+ combobox
63
88
  searchbox
64
89
  heading
65
90
  WebArea
@@ -93,6 +118,7 @@ Attribute selectors match values inside `node.attributes`. The special virtual a
93
118
  | `[attr^="prefix"]` | starts with |
94
119
  | `[attr$="suffix"]` | ends with |
95
120
  | `[attr*="sub"]` | contains substring |
121
+ | `[attr~="word"]` | contains `word` as a whole space-separated word |
96
122
 
97
123
  ```
98
124
  link[href]
@@ -100,6 +126,7 @@ button[disabled="true"]
100
126
  link[href^="https"]
101
127
  link[href$=".com"]
102
128
  link[href*="example"]
129
+ button[name~="Submit"]
103
130
  heading[name="Welcome"]
104
131
  link[name="Click \"here\""]
105
132
  ```
@@ -110,15 +137,56 @@ link[name="Click \"here\""]
110
137
  |--------|-----------|
111
138
  | `A B` | B is a descendant of A (any depth) |
112
139
  | `A > B` | B is a direct child of A |
140
+ | `A + B` | B is the immediately following sibling of A |
141
+ | `A ~ B` | B is any following sibling of A |
113
142
  | `A, B` | union — matches A or B |
114
143
 
115
144
  ```
116
145
  WebArea link
117
146
  main > button
147
+ label + textbox
148
+ link ~ link
118
149
  heading, button
119
150
  RootWebArea > link[href^="https"]
120
151
  ```
121
152
 
153
+ ### Positional pseudo-classes
154
+
155
+ Narrow a match by position within the parent's children array. Indexing is 1-based; the root node never matches a positional pseudo-class.
156
+
157
+ | Syntax | Semantics |
158
+ |--------|-----------|
159
+ | `:first-child` | node is the first child of its parent |
160
+ | `:last-child` | node is the last child of its parent |
161
+ | `:nth-child(n)` | node is the nth child (1-based) |
162
+
163
+ ```
164
+ link:first-child
165
+ button:last-child
166
+ menuitem:nth-child(2)
167
+ ```
168
+
169
+ ### Functional pseudo-classes
170
+
171
+ Take a comma-separated selector list inside parentheses. The argument list itself supports the full selector language and may be nested.
172
+
173
+ | Syntax | Semantics |
174
+ |--------|-----------|
175
+ | `:is(s1, s2, …)` | node matches any selector in the list |
176
+ | `:where(s1, s2, …)` | alias of `:is()` (no specificity in this engine) |
177
+ | `:not(s1, s2, …)` | node matches none of the selectors |
178
+ | `:has(s1, s2, …)` | node has a descendant matching any selector |
179
+
180
+ `:has()` walks descendants of the candidate node (excluding the node itself). Relative leading combinators (e.g. `:has(> link)`) are not supported.
181
+
182
+ ```
183
+ :is(heading, button)
184
+ link:not(:first-child)
185
+ *:has(button)
186
+ *:not(:has(link))
187
+ main > *:not(button)
188
+ ```
189
+
122
190
  ### Examples
123
191
 
124
192
  Sample accessibility tree:
@@ -136,58 +204,74 @@ uid=1 WebArea "Main Page"
136
204
 
137
205
  Example queries on it:
138
206
  - `link` matches all the links (uid=4, uid=7, uid=8)
139
- - 'navigation > link' matches only the links that are direct children of navigation (uid=7, uid=8)
207
+ - `navigation > link` matches only the links that are direct children of navigation (uid=7, uid=8)
140
208
  - `link[href^="https"]` matches links with an external href (uid=4)
141
209
  - `button[name="Submit"]` matches the submit button by name (uid=5)
142
210
  - `*[disabled="true"]` matches any disabled element (uid=5)
143
211
  - `heading, button` matches both headings and buttons in one query (uid=3, uid=5)
144
212
  - `#7` matches a node by its UID (uid=7)
213
+ - `link:first-child` matches uid=4 and uid=7 (first child of `main` and `navigation`)
214
+ - `link + button` matches uid=5 (button immediately after a link)
215
+ - `:is(heading, button)` is equivalent to `heading, button` (uid=3, uid=5)
216
+ - `link:not([href^="https"])` matches the relative-href links (uid=7, uid=8)
217
+ - `*:has(button)` matches ancestors of a button (uid=1, uid=2)
145
218
 
146
219
  ## Inspection
147
220
 
148
221
  - `query_selectors` is the most efficient way to get specific elements or data from the page. Use it instead of `take_snapshot` whenever possible.
149
222
  - By default, `query_selectors` returns the first match per selector (cheaper, less output). Pass `-a, --all` when you need every match — pair it with `--limit` to cap results per selector.
223
+ - Use `--wa, --with-ancestors` to include each match's ancestor chain in the result, and `--wc, --with-children` to include the descendant subtree of each match.
150
224
 
151
225
  ```bash
152
226
  # Query the accessibility tree returning the FIRST match per selector (--selector is repeatable)
153
- npx fastbrowser_cli query_selectors --selector "button" --selector "link"
227
+ npx fastbrowser_cli@latest query_selectors --selector "button" --selector "link"
154
228
 
155
- # Exclude ancestor nodes from the result
156
- npx fastbrowser_cli query_selectors --selector 'heading[level="1"]' --no-with-ancestors
229
+ # Include ancestor nodes in the result
230
+ npx fastbrowser_cli@latest query_selectors --selector 'heading[level="1"]' --with-ancestors
157
231
 
158
- # Per-selector control over withAncestors via JSON
159
- npx fastbrowser_cli query_selectors \
160
- --selectors-json '[{"selector":"button","withAncestors":true},{"selector":"link","withAncestors":false}]'
232
+ # Include the descendant subtree of each match
233
+ npx fastbrowser_cli@latest query_selectors --selector 'main' --with-children
234
+
235
+ # Per-selector control over withAncestors / withChildren via JSON
236
+ npx fastbrowser_cli@latest query_selectors \
237
+ --selectors-json '[{"selector":"button","withAncestors":true},{"selector":"link","withChildren":true}]'
161
238
 
162
239
  # Pass --all to return every match per selector; --limit caps results per selector (0 = unlimited)
163
- npx fastbrowser_cli query_selectors --all --selector "button" --selector "link" --limit 5
240
+ npx fastbrowser_cli@latest query_selectors --all --selector "button" --selector "link" --limit 5
164
241
 
165
- # Exclude ancestor nodes from the result
166
- npx fastbrowser_cli query_selectors --all --selector 'heading[level="1"]' --no-with-ancestors
242
+ # Include ancestor nodes in the result
243
+ npx fastbrowser_cli@latest query_selectors --all --selector 'heading[level="1"]' --with-ancestors
167
244
 
168
- # Per-selector control over limit / withAncestors via JSON (with --all)
169
- npx fastbrowser_cli query_selectors --all \
170
- --selectors-json '[{"selector":"button","limit":3,"withAncestors":true},{"selector":"link","limit":0,"withAncestors":false}]'
245
+ # Per-selector control over limit / withAncestors / withChildren via JSON (with --all)
246
+ npx fastbrowser_cli@latest query_selectors --all \
247
+ --selectors-json '[{"selector":"button","limit":3,"withAncestors":true},{"selector":"link","limit":0,"withChildren":true}]'
171
248
 
172
249
  # Take an accessibility-tree full page snapshot of the current page - very expensive, prefer targeted queries when possible
173
- npx fastbrowser_cli take_snapshot
250
+ npx fastbrowser_cli@latest take_snapshot
174
251
  ```
175
252
 
176
253
  ## Interaction
177
254
 
178
255
  ```bash
179
256
  # Click by a direct uid reference (fast path - no accessibility-tree lookup)
180
- npx fastbrowser_cli click --selector "#1_42"
257
+ npx fastbrowser_cli@latest click --selector "#1_42"
181
258
 
182
259
  # Click by any CSS-like selector - resolved to a uid internally
183
- npx fastbrowser_cli click -s 'button[name="Submit"]'
260
+ npx fastbrowser_cli@latest click -s 'button[name="Submit"]'
184
261
 
185
262
  # Fill a single form field - selector can be a uid (#1_7) or any CSS-like selector
186
- npx fastbrowser_cli fill_form -s 'textbox[name="Email"]' -v "hello@example.com"
263
+ npx fastbrowser_cli@latest fill_form -s 'textbox[name="Email"]' -v "hello@example.com"
187
264
 
188
265
  # Press a comma-separated sequence of keys (literals and named keys both work)
189
- npx fastbrowser_cli press_keys --keys "Tab, Tab, Enter"
190
- npx fastbrowser_cli press_keys --keys "Hello, Tab, Enter"
266
+ npx fastbrowser_cli@latest press_keys --keys "Tab, Tab, Enter"
267
+ npx fastbrowser_cli@latest press_keys --keys "Hello, Tab, Enter"
268
+
269
+ # Evaluate a JS function in the page context and get its JSON-able return value
270
+ npx fastbrowser_cli@latest evaluate_script --script 'function() { return { title: document.title, url: location.href }; }'
271
+
272
+ # Same, reading the function from a file or piped stdin
273
+ npx fastbrowser_cli@latest evaluate_script ./my_script.js
274
+ echo 'function() { return document.querySelectorAll("a").length; }' | npx fastbrowser_cli@latest evaluate_script
191
275
  ```
192
276
 
193
277
  ## Batch Execution
@@ -204,16 +288,16 @@ By default, the batch stops at the first failing line (shell `set -e` semantics)
204
288
 
205
289
  ```bash
206
290
  # From a file
207
- npx fastbrowser_cli batch ./demo.fbs
291
+ npx fastbrowser_cli@latest batch ./demo.fbs
208
292
 
209
293
  # Piped on stdin
210
- cat demo.fbs | npx fastbrowser_cli batch
294
+ cat demo.fbs | npx fastbrowser_cli@latest batch
211
295
 
212
296
  # Inline script
213
- npx fastbrowser_cli batch --script $'press_keys --keys "Enter"\nclick -s \'button[name^="Tout effacer"]\''
297
+ npx fastbrowser_cli@latest batch --script $'press_keys --keys "Enter"\nclick -s \'button[name^="Tout effacer"]\''
214
298
 
215
299
  # Continue through failures
216
- npx fastbrowser_cli batch --no-stop-on-error ./demo.fbs
300
+ npx fastbrowser_cli@latest batch --no-stop-on-error ./demo.fbs
217
301
  ```
218
302
 
219
303
  Example `demo.fbs`:
@@ -238,10 +322,13 @@ press_keys --keys "Tab, Enter"
238
322
  | `click` | Click an element by accessibility selector | `--selector` / `-s` |
239
323
  | `fill_form` | Fill a form field by accessibility selector | `--selector` / `-s`, `--value` |
240
324
  | `press_keys` | Press a comma-separated key sequence | `--keys` |
325
+ | `evaluate_script` | Run a JS function in the page context and return its JSON-able result | one of: `<file>`, `--script`, or piped stdin |
241
326
  | `batch` | Run multiple commands from a file, piped stdin, or `--script` inline | one of: `<file>`, `--script`, or piped stdin |
327
+ | `install [skill-folder]` | Install bundled skills into `<skill-folder>/skills/` (default: `.`) | — |
242
328
  | `server start` | Start the HTTP server daemon | — |
243
329
  | `server status` | Report server running/stopped | — |
244
330
  | `server stop` | Stop the HTTP server | — |
331
+ | `server restart` | Restart the HTTP server (re-establishes the MCP connection) | — |
245
332
 
246
333
  ## Output & Errors
247
334
 
@@ -0,0 +1,49 @@
1
+ import { execSync } from 'node:child_process';
2
+
3
+ const __dirname = new URL('.', import.meta.url).pathname;
4
+
5
+ export class FastBrowserHelper {
6
+ static async run(command: string): Promise<string> {
7
+ // const fullCommand = `npx fastbrowser_cli ${command}`;
8
+ const fullCommand = `npx tsx ${__dirname}../../fastbrowser_cli/fastbrowser_cli.ts ${command}`;
9
+ console.error(`Running command: ${fullCommand}`);
10
+ return execSync(fullCommand, { encoding: 'utf8' });
11
+ }
12
+
13
+ static async navigatePage(url: string): Promise<void> {
14
+ await FastBrowserHelper.run(`navigate_page --url '${url}'`);
15
+ }
16
+
17
+ static async fillForm(selector: string, value: string): Promise<void> {
18
+ await FastBrowserHelper.run(`fill_form --selector '${selector}' --value '${value}'`);
19
+ }
20
+
21
+ static async pressKeys(keys: string): Promise<void> {
22
+ await FastBrowserHelper.run(`press_keys --keys '${keys}'`);
23
+ }
24
+
25
+ static async click(selector: string): Promise<void> {
26
+ await FastBrowserHelper.run(`click -s '${selector}'`);
27
+ }
28
+
29
+ static async querySelectorsAll(selector: string, limit: number): Promise<string> {
30
+ return await FastBrowserHelper.run(`query_selectors --all --selector '${selector}' --limit ${limit}`);
31
+ }
32
+
33
+ static async querySelectorsAllWithChildren(selector: string, limit: number): Promise<string> {
34
+ return await FastBrowserHelper.run(`query_selectors --all --selector '${selector}' --limit ${limit} --with-ancestors --with-children`);
35
+ }
36
+
37
+ static async takeSnapshot(): Promise<string> {
38
+ return await FastBrowserHelper.run('take_snapshot');
39
+ }
40
+
41
+ static async querySelectors(selector: string, withAncestors = true): Promise<string> {
42
+ const flag = withAncestors === false ? ' --no-with-ancestors' : '';
43
+ return await FastBrowserHelper.run(`query_selectors --selector '${selector}'${flag}`);
44
+ }
45
+
46
+ static async evaluateScript(functionText: string): Promise<string> {
47
+ return await FastBrowserHelper.run(`evaluate_script --script "${functionText}"`);
48
+ }
49
+ }
@@ -0,0 +1,80 @@
1
+ # linkedin_cli
2
+
3
+ Command-line tool to interact with LinkedIn through a real browser session, driven by `fastbrowser_cli`.
4
+
5
+ It lets you create posts on the feed and manage direct-message conversations (list, read, send) from the terminal.
6
+
7
+ ## Requirements
8
+
9
+ - A running `fastbrowser_cli` session already authenticated on `linkedin.com`.
10
+ - The `_shared/fastbrowser_helper.ts` wrapper located in the sibling `contribs/_shared/` folder.
11
+
12
+ ## Usage
13
+
14
+ ```bash
15
+ npx tsx ./src/cli.ts <command> [args]
16
+ ```
17
+
18
+ ### Commands
19
+
20
+ | Command | Description |
21
+ |---------|-------------|
22
+ | `post <content>` | Create a post on the LinkedIn feed. |
23
+ | `dm_page` | Navigate to the LinkedIn messaging page. Run this first before any `dm_*` command. |
24
+ | `dm_list` | List the names of people you have conversations with. |
25
+ | `dm_send <target_user> <message>` | Send a message in an existing conversation with `target_user`. |
26
+ | `dm_thread <target_user>` | Print the message thread of a conversation with `target_user`. |
27
+ | `profile <slug>` | Export a LinkedIn profile by slug (the path component of `/in/<slug>/`). Use `-f json` for JSON output. |
28
+
29
+ `target_user` is matched against the conversation heading via a prefix match (e.g. `"Jerome"` matches `"Jerome Etienne"`).
30
+
31
+ ### Examples
32
+
33
+ Create a post:
34
+
35
+ ```bash
36
+ npx tsx ./src/cli.ts post "Hello LinkedIn from the CLI"
37
+ ```
38
+
39
+ List your conversations:
40
+
41
+ ```bash
42
+ npx tsx ./src/cli.ts dm_page
43
+ npx tsx ./src/cli.ts dm_list
44
+ ```
45
+
46
+ Read a thread:
47
+
48
+ ```bash
49
+ npx tsx ./src/cli.ts dm_thread "Jerome Etienne"
50
+ ```
51
+
52
+ Output is one message per line in the form:
53
+
54
+ ```
55
+ 2026-05-03T14:32:00:Jerome Etienne: hey, are you free this week?
56
+ ```
57
+
58
+ Send a reply:
59
+
60
+ ```bash
61
+ npx tsx ./src/cli.ts dm_send "Jerome Etienne" "Sure, Thursday afternoon works."
62
+ ```
63
+
64
+ Export a profile as markdown (default):
65
+
66
+ ```bash
67
+ npx tsx ./src/cli.ts profile jeromeetienne
68
+ ```
69
+
70
+ Export the same profile as JSON:
71
+
72
+ ```bash
73
+ npx tsx ./src/cli.ts profile jeromeetienne -f json
74
+ ```
75
+
76
+ ## Layout
77
+
78
+ - [src/cli.ts](src/cli.ts) — Commander entry point, defines the commands and orchestrates browser actions through `FastBrowserHelper`.
79
+ - [src/libs/linkedin_thread_helper.ts](src/libs/linkedin_thread_helper.ts) — Parses the LinkedIn message thread from an accessibility tree snapshot into timestamped lines.
80
+ - [src/libs/linkedin_profile_helper.ts](src/libs/linkedin_profile_helper.ts) — Parses a LinkedIn profile snapshot into a typed `LinkedinProfile` (header, About, Experience, Education) with markdown rendering.