fastbrowser_cli 1.0.37 → 1.0.40

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 (98) hide show
  1. package/dist/contribs/_shared/fastbrowser_helper.d.ts +13 -0
  2. package/dist/contribs/_shared/fastbrowser_helper.d.ts.map +1 -0
  3. package/dist/contribs/_shared/fastbrowser_helper.js +44 -0
  4. package/dist/contribs/_shared/fastbrowser_helper.js.map +1 -0
  5. package/dist/contribs/linkedin_cli/src/cli.d.ts +3 -0
  6. package/dist/contribs/linkedin_cli/src/cli.d.ts.map +1 -0
  7. package/dist/contribs/linkedin_cli/src/cli.js +299 -0
  8. package/dist/contribs/linkedin_cli/src/cli.js.map +1 -0
  9. package/dist/contribs/linkedin_cli/src/libs/linkedin_profile_helper.d.ts +73 -0
  10. package/dist/contribs/linkedin_cli/src/libs/linkedin_profile_helper.d.ts.map +1 -0
  11. package/dist/contribs/linkedin_cli/src/libs/linkedin_profile_helper.js +866 -0
  12. package/dist/contribs/linkedin_cli/src/libs/linkedin_profile_helper.js.map +1 -0
  13. package/dist/contribs/linkedin_cli/src/libs/linkedin_recent_posts_helper.d.ts +61 -0
  14. package/dist/contribs/linkedin_cli/src/libs/linkedin_recent_posts_helper.d.ts.map +1 -0
  15. package/dist/contribs/linkedin_cli/src/libs/linkedin_recent_posts_helper.js +885 -0
  16. package/dist/contribs/linkedin_cli/src/libs/linkedin_recent_posts_helper.js.map +1 -0
  17. package/dist/contribs/linkedin_cli/src/libs/linkedin_thread_helper.d.ts +11 -0
  18. package/dist/contribs/linkedin_cli/src/libs/linkedin_thread_helper.d.ts.map +1 -0
  19. package/dist/contribs/linkedin_cli/src/libs/linkedin_thread_helper.js +145 -0
  20. package/dist/contribs/linkedin_cli/src/libs/linkedin_thread_helper.js.map +1 -0
  21. package/dist/contribs/twitter_cli/src/cli.d.ts +3 -0
  22. package/dist/contribs/twitter_cli/src/cli.d.ts.map +1 -0
  23. package/dist/contribs/twitter_cli/src/cli.js +273 -0
  24. package/dist/contribs/twitter_cli/src/cli.js.map +1 -0
  25. package/dist/contribs/twitter_cli/src/libs/twitter_profile_helper.d.ts +28 -0
  26. package/dist/contribs/twitter_cli/src/libs/twitter_profile_helper.d.ts.map +1 -0
  27. package/dist/contribs/twitter_cli/src/libs/twitter_profile_helper.js +274 -0
  28. package/dist/contribs/twitter_cli/src/libs/twitter_profile_helper.js.map +1 -0
  29. package/dist/contribs/twitter_cli/src/libs/twitter_recent_posts_helper.d.ts +43 -0
  30. package/dist/contribs/twitter_cli/src/libs/twitter_recent_posts_helper.d.ts.map +1 -0
  31. package/dist/contribs/twitter_cli/src/libs/twitter_recent_posts_helper.js +519 -0
  32. package/dist/contribs/twitter_cli/src/libs/twitter_recent_posts_helper.js.map +1 -0
  33. package/dist/contribs/twitter_cli/src/libs/twitter_thread_helper.d.ts +11 -0
  34. package/dist/contribs/twitter_cli/src/libs/twitter_thread_helper.d.ts.map +1 -0
  35. package/dist/contribs/twitter_cli/src/libs/twitter_thread_helper.js +213 -0
  36. package/dist/contribs/twitter_cli/src/libs/twitter_thread_helper.js.map +1 -0
  37. package/dist/fastbrowser_cli/fastbrowser_cli.js +43 -0
  38. package/dist/fastbrowser_cli/fastbrowser_cli.js.map +1 -1
  39. package/dist/fastbrowser_httpd/libs/tool-schemas.d.ts +4 -0
  40. package/dist/fastbrowser_httpd/libs/tool-schemas.d.ts.map +1 -1
  41. package/dist/fastbrowser_httpd/libs/tool-schemas.js +4 -0
  42. package/dist/fastbrowser_httpd/libs/tool-schemas.js.map +1 -1
  43. package/dist/fastbrowser_mcp/fastbrowser_mcp.js +36 -2
  44. package/dist/fastbrowser_mcp/fastbrowser_mcp.js.map +1 -1
  45. package/dist/fastbrowser_mcp/libs/mcp_target_helper.d.ts +2 -0
  46. package/dist/fastbrowser_mcp/libs/mcp_target_helper.d.ts.map +1 -1
  47. package/dist/fastbrowser_mcp/libs/mcp_target_helper.js +12 -0
  48. package/dist/fastbrowser_mcp/libs/mcp_target_helper.js.map +1 -1
  49. package/dist/fastbrowser_mcp/libs/response_formatter.d.ts +1 -0
  50. package/dist/fastbrowser_mcp/libs/response_formatter.d.ts.map +1 -1
  51. package/dist/fastbrowser_mcp/libs/response_formatter.js +27 -0
  52. package/dist/fastbrowser_mcp/libs/response_formatter.js.map +1 -1
  53. package/dist/shared/fastbrowser_helper.d.ts +13 -0
  54. package/dist/shared/fastbrowser_helper.d.ts.map +1 -0
  55. package/dist/shared/fastbrowser_helper.js +39 -0
  56. package/dist/shared/fastbrowser_helper.js.map +1 -0
  57. package/examples/linkedin_cli_TOREMOVE/README.md +7 -0
  58. package/examples/{linkedin_cli → linkedin_cli_TOREMOVE}/linkedin_dm.sh +8 -4
  59. package/examples/linkedin_cli_TOREMOVE/linkedin_dm.ts +326 -0
  60. package/examples/linkedin_cli_TOREMOVE/linkedin_dm_messages.ts +279 -0
  61. package/examples/linkedin_cli_TOREMOVE/linkedin_full_cycle.sh +5 -0
  62. package/examples/{linkedin_cli → linkedin_cli_TOREMOVE}/linkedin_post.sh +3 -0
  63. package/examples/linkedin_cli_TOREMOVE/message_thread.a11y.txt +252 -0
  64. package/listitem +4 -0
  65. package/package.json +7 -3
  66. package/skills/fastbrowser/SKILL.md +33 -25
  67. package/src/contribs/_shared/fastbrowser_helper.ts +54 -0
  68. package/src/contribs/linkedin_cli/README.md +80 -0
  69. package/src/contribs/linkedin_cli/data/linkedin_posts_jeromeetienne.a11y.txt +2364 -0
  70. package/src/contribs/linkedin_cli/data/linkedin_posts_jontwigge.a11y.txt +2740 -0
  71. package/src/contribs/linkedin_cli/data/linkedin_posts_julien_guezennec.a11y.txt +2073 -0
  72. package/src/contribs/linkedin_cli/data/linkedin_profile_jeromeetienne.a11y.txt +1863 -0
  73. package/src/contribs/linkedin_cli/data/linkedin_profile_jontwigge.a11y.txt +1738 -0
  74. package/src/contribs/linkedin_cli/data/linkedin_profile_julien_guezennec.a11y.txt +2182 -0
  75. package/src/contribs/linkedin_cli/src/cli.ts +345 -0
  76. package/src/contribs/linkedin_cli/src/libs/linkedin_profile_helper.ts +964 -0
  77. package/src/contribs/linkedin_cli/src/libs/linkedin_recent_posts_helper.ts +982 -0
  78. package/src/contribs/linkedin_cli/src/libs/linkedin_thread_helper.ts +171 -0
  79. package/src/contribs/twitter_cli/README.md +79 -0
  80. package/src/contribs/twitter_cli/data/twitter_chat.a11y.txt +215 -0
  81. package/src/contribs/twitter_cli/data/twitter_home.a11y.txt +467 -0
  82. package/src/contribs/twitter_cli/data/twitter_profile.a11y.txt +418 -0
  83. package/src/contribs/twitter_cli/data/twitter_profile_jontwigge.a11y.txt +484 -0
  84. package/src/contribs/twitter_cli/data/twitter_profile_molokoloco.a11y.txt +483 -0
  85. package/src/contribs/twitter_cli/src/cli.ts +315 -0
  86. package/src/contribs/twitter_cli/src/libs/twitter_profile_helper.ts +328 -0
  87. package/src/contribs/twitter_cli/src/libs/twitter_recent_posts_helper.ts +607 -0
  88. package/src/contribs/twitter_cli/src/libs/twitter_thread_helper.ts +240 -0
  89. package/src/fastbrowser_cli/fastbrowser_cli.ts +51 -0
  90. package/src/fastbrowser_httpd/libs/tool-schemas.ts +6 -0
  91. package/src/fastbrowser_mcp/fastbrowser_mcp.ts +46 -3
  92. package/src/fastbrowser_mcp/libs/mcp_target_helper.ts +11 -0
  93. package/src/fastbrowser_mcp/libs/response_formatter.ts +29 -0
  94. package/src/shared/fastbrowser_helper.ts +49 -0
  95. package/tsconfig.json +1 -1
  96. package/examples/mcp_client_playwright.ts +0 -34
  97. /package/examples/{linkedin_cli → linkedin_cli_TOREMOVE}/linkedin.snapshot.txt +0 -0
  98. /package/examples/{twitter_cli → twitter_cli_TOREMOVE}/twitter_post.sh +0 -0
@@ -0,0 +1,279 @@
1
+ import { Command } from 'commander';
2
+ import Fs from 'fs';
3
+
4
+ import { A11yDisplay, A11yQuery, A11yTree, AxNode } from 'a11y_parse';
5
+
6
+ class MessagesThreadHelper {
7
+
8
+
9
+ ///////////////////////////////////////////////////////////////////////////////
10
+ ///////////////////////////////////////////////////////////////////////////////
11
+ //
12
+ ///////////////////////////////////////////////////////////////////////////////
13
+ ///////////////////////////////////////////////////////////////////////////////
14
+
15
+ static async parseMessagesThread(
16
+ axNodeThread: AxNode,
17
+ overrideYear?: number,
18
+ ): Promise<string> {
19
+ const lines: string[] = [];
20
+ let currentDate: Date | null = null;
21
+ let currentSender: string | null = null;
22
+ let currentTime: { hours: number; minutes: number } | null = null;
23
+ const year = overrideYear !== undefined ? overrideYear : new Date().getFullYear();
24
+
25
+ for (const item of axNodeThread.children) {
26
+ if (item.children.length === 0) {
27
+ continue;
28
+ }
29
+
30
+ const dateChild = item.children.find(
31
+ (c) => c.role === 'time'
32
+ && c.attributes['value'] !== undefined
33
+ && c.attributes['value'].startsWith('•') === false,
34
+ );
35
+ if (dateChild !== undefined && dateChild.attributes['value'] !== undefined) {
36
+ currentDate = MessagesThreadHelper.parseDateMarker(dateChild.attributes['value'], year);
37
+ }
38
+
39
+ const headerChild = item.children.find(
40
+ (c) => c.role === 'generic'
41
+ && c.attributes['value'] !== undefined
42
+ && / sent the following messages at /.test(c.attributes['value']),
43
+ );
44
+ if (headerChild !== undefined && headerChild.attributes['value'] !== undefined) {
45
+ const parsed = MessagesThreadHelper.extractSenderFromHeader(headerChild.attributes['value']);
46
+ if (parsed !== null) {
47
+ currentSender = parsed.sender;
48
+ currentTime = MessagesThreadHelper.parseTimeOfDay(parsed.time);
49
+ }
50
+ }
51
+
52
+ const innerTimes = A11yQuery.querySelectorAll(item, 'time');
53
+ const bulletTime = innerTimes.find(
54
+ (t) => t.attributes['value'] !== undefined && t.attributes['value'].startsWith('•'),
55
+ );
56
+ if (bulletTime !== undefined && bulletTime.attributes['value'] !== undefined) {
57
+ currentTime = MessagesThreadHelper.parseTimeOfDay(bulletTime.attributes['value']);
58
+ }
59
+
60
+ if (headerChild === undefined) {
61
+ const linkNodes = A11yQuery.querySelectorAll(item, 'link[url*="linkedin.com/in/"]');
62
+ const senderLink = linkNodes.find(
63
+ (l) => l.name !== undefined
64
+ && l.name.length > 0
65
+ && l.name.startsWith('View ') === false,
66
+ );
67
+ if (senderLink !== undefined && senderLink.name !== undefined) {
68
+ currentSender = senderLink.name;
69
+ }
70
+ }
71
+
72
+ const paragraphs = A11yQuery.querySelectorAll(item, 'paragraph')
73
+ .filter((p) => MessagesThreadHelper.hasButtonAncestor(p, item) === false);
74
+ const texts = paragraphs
75
+ .map((p) => MessagesThreadHelper.extractParagraphText(p))
76
+ .filter((t) => t.length > 0);
77
+ if (texts.length === 0) {
78
+ continue;
79
+ }
80
+ if (currentDate === null || currentSender === null || currentTime === null) {
81
+ continue;
82
+ }
83
+
84
+ const iso = MessagesThreadHelper.combineDateTime(currentDate, currentTime);
85
+ lines.push(`${iso}:${currentSender}: ${texts.join(' ')}`);
86
+ }
87
+
88
+ return lines.join('\n');
89
+ }
90
+
91
+ ///////////////////////////////////////////////////////////////////////////////
92
+ ///////////////////////////////////////////////////////////////////////////////
93
+ //
94
+ ///////////////////////////////////////////////////////////////////////////////
95
+ ///////////////////////////////////////////////////////////////////////////////
96
+
97
+ private static parseDateMarker(value: string, fallbackYear: number): Date {
98
+ const months: Record<string, number> = {
99
+ Jan: 0, Feb: 1, Mar: 2, Apr: 3, May: 4, Jun: 5,
100
+ Jul: 6, Aug: 7, Sep: 8, Oct: 9, Nov: 10, Dec: 11,
101
+ };
102
+ const match = value.match(/^([A-Za-z]+)\s+(\d{1,2})(?:,\s*(\d{4}))?$/);
103
+ if (match === null) {
104
+ throw new Error(`Cannot parse date marker: "${value}"`);
105
+ }
106
+ const monthIndex = months[match[1]];
107
+ if (monthIndex === undefined) {
108
+ throw new Error(`Unknown month name: "${match[1]}"`);
109
+ }
110
+ const year = match[3] !== undefined ? parseInt(match[3], 10) : fallbackYear;
111
+ return new Date(year, monthIndex, parseInt(match[2], 10));
112
+ }
113
+
114
+ private static parseTimeOfDay(value: string): { hours: number; minutes: number } {
115
+ const cleaned = value.replace(/^•\s*/, '').trim();
116
+ const match = cleaned.match(/^(\d{1,2}):(\d{2})\s*(AM|PM)$/i);
117
+ if (match === null) {
118
+ throw new Error(`Cannot parse time of day: "${value}"`);
119
+ }
120
+ let hours = parseInt(match[1], 10);
121
+ const minutes = parseInt(match[2], 10);
122
+ const meridiem = match[3].toUpperCase();
123
+ if (meridiem === 'AM' && hours === 12) {
124
+ hours = 0;
125
+ } else if (meridiem === 'PM' && hours !== 12) {
126
+ hours += 12;
127
+ }
128
+ return { hours, minutes };
129
+ }
130
+
131
+ private static combineDateTime(date: Date, time: { hours: number; minutes: number }): string {
132
+ const yyyy = date.getFullYear().toString().padStart(4, '0');
133
+ const mm = (date.getMonth() + 1).toString().padStart(2, '0');
134
+ const dd = date.getDate().toString().padStart(2, '0');
135
+ const hh = time.hours.toString().padStart(2, '0');
136
+ const mi = time.minutes.toString().padStart(2, '0');
137
+ return `${yyyy}-${mm}-${dd}T${hh}:${mi}:00`;
138
+ }
139
+
140
+ private static extractParagraphText(paragraph: AxNode): string {
141
+ if (paragraph.attributes['value'] !== undefined) {
142
+ return paragraph.attributes['value'].trim();
143
+ }
144
+ const parts: string[] = [];
145
+ for (const child of paragraph.children) {
146
+ if (child.name !== undefined && child.name.length > 0) {
147
+ parts.push(child.name);
148
+ continue;
149
+ }
150
+ if (child.attributes['value'] !== undefined) {
151
+ parts.push(child.attributes['value']);
152
+ }
153
+ }
154
+ return parts.join(' ').trim();
155
+ }
156
+
157
+ private static extractSenderFromHeader(value: string): { sender: string; time: string } | null {
158
+ const match = value.match(/^(.+?) sent the following messages at (.+)$/);
159
+ if (match === null) {
160
+ return null;
161
+ }
162
+ return { sender: match[1], time: match[2] };
163
+ }
164
+
165
+ private static hasButtonAncestor(node: AxNode, stopAt: AxNode): boolean {
166
+ let current: AxNode | undefined = node.parent;
167
+ while (current !== undefined && current.uid !== stopAt.uid) {
168
+ if (current.role === 'button') {
169
+ return true;
170
+ }
171
+ current = current.parent;
172
+ }
173
+ return false;
174
+ }
175
+ }
176
+
177
+ ///////////////////////////////////////////////////////////////////////////////
178
+ ///////////////////////////////////////////////////////////////////////////////
179
+ //
180
+ ///////////////////////////////////////////////////////////////////////////////
181
+ ///////////////////////////////////////////////////////////////////////////////
182
+
183
+ class MainHelper {
184
+
185
+ ///////////////////////////////////////////////////////////////////////////////
186
+ ///////////////////////////////////////////////////////////////////////////////
187
+ //
188
+ ///////////////////////////////////////////////////////////////////////////////
189
+ ///////////////////////////////////////////////////////////////////////////////
190
+
191
+ static async readFileContent(filePath: string): Promise<string> {
192
+ if (filePath !== '-') {
193
+ const fileContent = await Fs.promises.readFile(filePath, 'utf-8');
194
+ return fileContent;
195
+ }
196
+
197
+ return await new Promise<string>((resolve, reject) => {
198
+ let data = '';
199
+ process.stdin.setEncoding('utf-8');
200
+ process.stdin.on('data', (chunk) => {
201
+ data += chunk;
202
+ });
203
+ process.stdin.on('end', () => {
204
+ resolve(data);
205
+ });
206
+ process.stdin.on('error', (err) => {
207
+ reject(err);
208
+ });
209
+ });
210
+ }
211
+
212
+ ///////////////////////////////////////////////////////////////////////////////
213
+ ///////////////////////////////////////////////////////////////////////////////
214
+ //
215
+ ///////////////////////////////////////////////////////////////////////////////
216
+ ///////////////////////////////////////////////////////////////////////////////
217
+
218
+ static async getAxNodeThread(axTree: AxNode): Promise<AxNode> {
219
+ const axNodeConvList: AxNode | undefined = A11yQuery.querySelector(axTree, 'list[name="Conversation List"]')
220
+ if (axNodeConvList === undefined) throw new Error('Could not find conversation list node');
221
+
222
+ console.log('axNodeConvList:', A11yDisplay.stringifyNode(axNodeConvList));
223
+ const axNodeConvListParent = axNodeConvList.parent
224
+ if (axNodeConvListParent === undefined) throw new Error('Conversation list node has no parent');
225
+
226
+ console.log('axNodeConvListParent:', A11yDisplay.stringifyNode(axNodeConvListParent));
227
+
228
+ // goto next sibling of conv list parent, which is the "Conversation details" container
229
+ const axNodeConvDetails: AxNode | undefined = A11yTree.nextSibling(axNodeConvListParent);
230
+ if (axNodeConvDetails === undefined) throw new Error('Could not find conversation details node');
231
+
232
+ console.log('axNodeConvDetails:', A11yDisplay.stringifyNode(axNodeConvDetails));
233
+
234
+ // console.log('Conversation List node:', axNodeConvList);
235
+ console.log('axNodeConvDetails:')
236
+ // console.log(A11yDisplay.stringifyTree(axNodeConvDetails));
237
+
238
+ const axNodeThread = A11yQuery.querySelector(axNodeConvDetails, 'list');
239
+ if (axNodeThread === undefined) throw new Error('Could not find thread node');
240
+
241
+ return axNodeThread;
242
+ }
243
+ }
244
+
245
+ ///////////////////////////////////////////////////////////////////////////////
246
+ ///////////////////////////////////////////////////////////////////////////////
247
+ //
248
+ ///////////////////////////////////////////////////////////////////////////////
249
+ ///////////////////////////////////////////////////////////////////////////////
250
+
251
+ async function main(): Promise<void> {
252
+ const program = new Command();
253
+
254
+ // NODE_OPTIONS='' NPM_CONFIG_LOGLEVEL=silent npm run dev:cli -- take_snapshot | npx tsx ./examples/linkedin_cli/linkedin_dm_messages.ts -f -
255
+ program
256
+ .name('linkedin_dm_messages')
257
+ .description('Linkedin DM messages CLI')
258
+ .requiredOption('-f, --a11y_file <a11y_path>', 'path to the a11y file')
259
+ .parse(process.argv);
260
+
261
+ const options = program.opts<{
262
+ a11y_file: string;
263
+ }>();
264
+
265
+ console.log('Options:', options);
266
+
267
+ // Get and parse the whole snapshot tree, then find the relevant subtree for the messages thread container and print it
268
+ const fileContent = await MainHelper.readFileContent(options.a11y_file);
269
+ const axTree = A11yTree.parse(fileContent);
270
+ const axNodeThread = axTree.role === 'list'
271
+ ? axTree
272
+ : await MainHelper.getAxNodeThread(axTree);
273
+
274
+ const transcript = await MessagesThreadHelper.parseMessagesThread(axNodeThread);
275
+ console.log(transcript);
276
+ }
277
+
278
+
279
+ void main()
@@ -0,0 +1,5 @@
1
+
2
+
3
+ npx tsx ./examples/linkedin_cli/linkedin_dm.ts list_convos
4
+ npx tsx ./examples/linkedin_cli/linkedin_dm.ts messages "Eric Defiez"
5
+ npx tsx ./examples/linkedin_cli/linkedin_dm.ts send "Eric Defiez" "Hello from linkedin_dm.ts"
@@ -1,5 +1,8 @@
1
1
  #!/bin/bash
2
2
 
3
+ # Check if the CLI is working properly
4
+ NODE_OPTIONS='' NPM_CONFIG_LOGLEVEL=silent npm run dev:cli -- check
5
+
3
6
  # Goto linkedin feed page using the CLI commands below:
4
7
  NODE_OPTIONS='' NPM_CONFIG_LOGLEVEL=silent npm run dev:cli -- navigate_page --url https://www.linkedin.com/feed/
5
8
 
@@ -0,0 +1,252 @@
1
+ uid=e481 list
2
+ uid=s46 listitem
3
+ uid=e482 listitem
4
+ uid=e483 time value="Apr 14"
5
+ uid=e484 generic value="Julien Guézennec sent the following messages at 3:52 PM"
6
+ uid=e485 generic
7
+ uid=s47 unknown url="https://www.linkedin.com/in/ACoAAADjYaoBKCGNtZN_m0INLC4MnN9yFkTDUqY"
8
+ uid=e487 generic value="View Julien’s profile"
9
+ uid=s48 unknown
10
+ uid=e489 generic
11
+ uid=e490 generic
12
+ uid=e491 link "Julien Guézennec" url="https://www.linkedin.com/in/ACoAAADjYaoBKCGNtZN_m0INLC4MnN9yFkTDUqY"
13
+ uid=e492 generic value="Julien Guézennec"
14
+ uid=e493 img "LinkedIn Verified"
15
+ uid=e495 time value="• 3:52 PM"
16
+ uid=e499 paragraph value="😝🙃😅 je suis dyschromatique ! On se moque pas des handicaps !"
17
+ uid=e500 listitem
18
+ uid=e501 generic
19
+ uid=s49 unknown url="https://www.linkedin.com/in/ACoAAADjYaoBKCGNtZN_m0INLC4MnN9yFkTDUqY"
20
+ uid=e503 generic value="View Julien’s profile"
21
+ uid=s50 unknown
22
+ uid=e505 generic
23
+ uid=e506 generic
24
+ uid=e507 link "Julien Guézennec" url="https://www.linkedin.com/in/ACoAAADjYaoBKCGNtZN_m0INLC4MnN9yFkTDUqY"
25
+ uid=e508 generic value="Julien Guézennec"
26
+ uid=e509 img "LinkedIn Verified"
27
+ uid=e511 time value="• 4:12 PM"
28
+ uid=e515 paragraph value="Just for you i have send my futur newsletter ! Full of flying colors for reaching the contact i have scrapped around my town with Claude ! Unfortunatly, see in attached image"
29
+ uid=e520 button "Click or press enter to display in the image preview"
30
+ uid=e521 generic value="Click or press enter to display in the image preview"
31
+ uid=e522 img
32
+ uid=e523 listitem
33
+ uid=e524 generic
34
+ uid=s51 unknown url="https://www.linkedin.com/in/ACoAAADjYaoBKCGNtZN_m0INLC4MnN9yFkTDUqY"
35
+ uid=e526 generic value="View Julien’s profile"
36
+ uid=s52 unknown
37
+ uid=e528 generic
38
+ uid=e529 generic
39
+ uid=e530 link "Julien Guézennec" url="https://www.linkedin.com/in/ACoAAADjYaoBKCGNtZN_m0INLC4MnN9yFkTDUqY"
40
+ uid=e531 generic value="Julien Guézennec"
41
+ uid=e532 img "LinkedIn Verified"
42
+ uid=e534 time value="• 4:13 PM"
43
+ uid=e538 paragraph
44
+ uid=s53 StaticText "Is that a good path ?"
45
+ uid=e539 link "https://julienweb.fr/blog/dashboard-local-node-claude-sqlite/9555/" url="https://julienweb.fr/blog/dashboard-local-node-claude-sqlite/9555/"
46
+ uid=e544 generic
47
+ uid=s54 unknown url="https://julienweb.fr/blog/dashboard-local-node-claude-sqlite/9555/"
48
+ uid=e551 link "J'ai construit mon dashboard local avec Claude — Node.js + SQLite" url="https://julienweb.fr/blog/dashboard-local-node-claude-sqlite/9555/"
49
+ uid=e554 generic
50
+ uid=e556 generic value="J'ai construit mon dashboard local avec Claude — Node.js + SQLite"
51
+ uid=e557 generic value="julienweb.fr"
52
+ uid=e558 listitem
53
+ uid=e559 generic
54
+ uid=e563 paragraph
55
+ uid=e564 link "https://julienweb.fr/wp-content/uploads/claude-dashboard-julienweb-fr-ia-2026-2.png" url="https://julienweb.fr/wp-content/uploads/claude-dashboard-julienweb-fr-ia-2026-2.png"
56
+ uid=e569 generic
57
+ uid=s55 unknown url="https://julienweb.fr/wp-content/uploads/claude-dashboard-julienweb-fr-ia-2026-2.png"
58
+ uid=e576 link "Web Link" url="https://julienweb.fr/wp-content/uploads/claude-dashboard-julienweb-fr-ia-2026-2.png"
59
+ uid=e579 generic
60
+ uid=e580 generic value="Web Link"
61
+ uid=e581 generic value="julienweb.fr"
62
+ uid=e582 listitem
63
+ uid=e583 generic
64
+ uid=s56 unknown url="https://www.linkedin.com/in/ACoAAADjYaoBKCGNtZN_m0INLC4MnN9yFkTDUqY"
65
+ uid=e585 generic value="View Julien’s profile"
66
+ uid=s57 unknown
67
+ uid=e587 generic
68
+ uid=e588 generic
69
+ uid=e589 link "Julien Guézennec" url="https://www.linkedin.com/in/ACoAAADjYaoBKCGNtZN_m0INLC4MnN9yFkTDUqY"
70
+ uid=e590 generic value="Julien Guézennec"
71
+ uid=e591 img "LinkedIn Verified"
72
+ uid=e593 time value="• 4:18 PM"
73
+ uid=e597 paragraph
74
+ uid=s58 StaticText "Regarde ca :)"
75
+ uid=e598 link "https://passerelles.economie.gouv.fr/offre-de-emploi/emploi-chef-de-projet-cafe-ia-relations-territoriales-sen-cian-219-h-f_29182.aspx" url="https://passerelles.economie.gouv.fr/offre-de-emploi/emploi-chef-de-projet-cafe-ia-relations-territoriales-sen-cian-219-h-f_29182.aspx"
76
+ uid=s59 StaticText "Je suis un ouf j'ai candidaté ^^ CV full IA ^^ LOL"
77
+ uid=e603 generic
78
+ uid=s60 unknown url="https://passerelles.economie.gouv.fr/offre-de-emploi/emploi-chef-de-projet-cafe-ia-relations-territoriales-sen-cian-219-h-f_29182.aspx"
79
+ uid=e610 link "Chef de projet Café IA - Relations territoriales SEN-CIAN-219 H/F" url="https://passerelles.economie.gouv.fr/offre-de-emploi/emploi-chef-de-projet-cafe-ia-relations-territoriales-sen-cian-219-h-f_29182.aspx"
80
+ uid=e613 generic
81
+ uid=e615 generic value="Chef de projet Café IA - Relations territoriales SEN-CIAN-219 H/F"
82
+ uid=e616 generic value="passerelles.economie.gouv.fr"
83
+ uid=e617 listitem
84
+ uid=e622 button "PDF CV Julien Guézennec — Chef de projet Café IA.pdf 1 MB Download"
85
+ uid=e623 generic
86
+ uid=s61 StaticText "PDF"
87
+ uid=e624 generic
88
+ uid=e625 paragraph value="CV Julien Guézennec — Chef de projet Café IA.pdf"
89
+ uid=e626 paragraph value="1 MB"
90
+ uid=e627 generic
91
+ uid=e628 img
92
+ uid=e630 paragraph value="Download"
93
+ uid=e631 listitem
94
+ uid=e632 generic value="Jerome Etienne sent the following messages at 6:42 PM"
95
+ uid=e633 generic
96
+ uid=e634 link "View Jerome’s profile Jerome Etienne" url="https://www.linkedin.com/in/ACoAAABV1ZYB_x7AQ_K0oe15LPzT32jClDzkqjQ"
97
+ uid=e635 generic value="View Jerome’s profile"
98
+ uid=e636 img "Jerome Etienne"
99
+ uid=e637 generic
100
+ uid=e638 generic
101
+ uid=e639 link "Jerome Etienne" url="https://www.linkedin.com/in/ACoAAABV1ZYB_x7AQ_K0oe15LPzT32jClDzkqjQ"
102
+ uid=e640 generic value="Jerome Etienne"
103
+ uid=e641 img "LinkedIn Verified"
104
+ uid=e643 time value="• 6:42 PM"
105
+ uid=e647 paragraph value="Se déplacer sur l’ensemble du territoire français pour rencontrer les collectivités, les conseillers numériques, et assurer notre présence lors d’événements dédiés à l’inclusion numérique et animer des cafés IA partout en France – déplacements réguliers à prévoir – environ 1 par semaine <- tu as vu ca ?"
106
+ uid=e648 list
107
+ uid=e649 listitem
108
+ uid=e650 button "React with 👍 1"
109
+ uid=e651 generic
110
+ uid=e652 generic value="React with"
111
+ uid=s62 generic value="👍"
112
+ uid=s63 generic value="1"
113
+ uid=e653 listitem
114
+ uid=e656 button "Open Emoji Keyboard"
115
+ uid=e657 listitem
116
+ uid=e662 paragraph value="tiens moi aussi je dois refaire un resume"
117
+ uid=e663 listitem
118
+ uid=e668 paragraph value="faudra que tu mexplique comment tu as fait"
119
+ uid=e669 listitem
120
+ uid=e670 generic value="Julien Guézennec sent the following messages at 6:59 PM"
121
+ uid=e671 generic
122
+ uid=s64 unknown url="https://www.linkedin.com/in/ACoAAADjYaoBKCGNtZN_m0INLC4MnN9yFkTDUqY"
123
+ uid=e673 generic value="View Julien’s profile"
124
+ uid=s65 unknown
125
+ uid=e675 generic
126
+ uid=e676 generic
127
+ uid=e677 link "Julien Guézennec" url="https://www.linkedin.com/in/ACoAAADjYaoBKCGNtZN_m0INLC4MnN9yFkTDUqY"
128
+ uid=e678 generic value="Julien Guézennec"
129
+ uid=e679 img "LinkedIn Verified"
130
+ uid=e681 time value="• 6:59 PM"
131
+ uid=e685 paragraph value="👍"
132
+ uid=e686 listitem
133
+ uid=e687 generic
134
+ uid=s66 unknown url="https://www.linkedin.com/in/ACoAAADjYaoBKCGNtZN_m0INLC4MnN9yFkTDUqY"
135
+ uid=e689 generic value="View Julien’s profile"
136
+ uid=s67 unknown
137
+ uid=e691 generic
138
+ uid=e692 generic
139
+ uid=e693 link "Julien Guézennec" url="https://www.linkedin.com/in/ACoAAADjYaoBKCGNtZN_m0INLC4MnN9yFkTDUqY"
140
+ uid=e694 generic value="Julien Guézennec"
141
+ uid=e695 img "LinkedIn Verified"
142
+ uid=e697 time value="• 7:38 PM"
143
+ uid=e701 paragraph
144
+ uid=e702 link "https://www.instagram.com/p/ChcTUsoD1rZ/" url="https://www.instagram.com/p/ChcTUsoD1rZ/"
145
+ uid=s68 StaticText "Go :)"
146
+ uid=e710 link "Julien Guézennec on Instagram" url="https://www.instagram.com/julienweb.fr/p/ChcTUsoD1rZ/"
147
+ uid=e712 generic
148
+ uid=e713 generic
149
+ uid=e715 generic value="Julien Guézennec on Instagram"
150
+ uid=e716 generic value="instagram.com"
151
+ uid=e717 generic value="27 likes, 3 comments - julienweb.fr on August 19, 2022"
152
+ uid=e718 listitem
153
+ uid=e719 generic value="Jerome Etienne sent the following messages at 8:16 PM"
154
+ uid=e720 generic
155
+ uid=e721 link "View Jerome’s profile Jerome Etienne" url="https://www.linkedin.com/in/ACoAAABV1ZYB_x7AQ_K0oe15LPzT32jClDzkqjQ"
156
+ uid=e722 generic value="View Jerome’s profile"
157
+ uid=e723 img "Jerome Etienne"
158
+ uid=e724 generic
159
+ uid=e725 generic
160
+ uid=e726 link "Jerome Etienne" url="https://www.linkedin.com/in/ACoAAABV1ZYB_x7AQ_K0oe15LPzT32jClDzkqjQ"
161
+ uid=e727 generic value="Jerome Etienne"
162
+ uid=e728 img "LinkedIn Verified"
163
+ uid=e730 time value="• 8:16 PM"
164
+ uid=e734 paragraph value="hehe"
165
+ uid=e735 listitem
166
+ uid=e740 paragraph value="et tu te vois represente letat ?"
167
+ uid=e741 listitem
168
+ uid=e742 generic value="Julien Guézennec sent the following messages at 8:18 PM"
169
+ uid=e743 generic
170
+ uid=s69 unknown url="https://www.linkedin.com/in/ACoAAADjYaoBKCGNtZN_m0INLC4MnN9yFkTDUqY"
171
+ uid=e745 generic value="View Julien’s profile"
172
+ uid=s70 unknown
173
+ uid=e747 generic
174
+ uid=e748 generic
175
+ uid=e749 link "Julien Guézennec" url="https://www.linkedin.com/in/ACoAAADjYaoBKCGNtZN_m0INLC4MnN9yFkTDUqY"
176
+ uid=e750 generic value="Julien Guézennec"
177
+ uid=e751 img "LinkedIn Verified"
178
+ uid=e753 time value="• 8:18 PM"
179
+ uid=e757 paragraph value="Yes ! Je suis :)"
180
+ uid=e758 listitem
181
+ uid=e759 time value="Apr 16"
182
+ uid=e760 generic
183
+ uid=s71 unknown url="https://www.linkedin.com/in/ACoAAADjYaoBKCGNtZN_m0INLC4MnN9yFkTDUqY"
184
+ uid=e762 generic value="View Julien’s profile"
185
+ uid=s72 unknown
186
+ uid=e764 generic
187
+ uid=e765 generic
188
+ uid=e766 link "Julien Guézennec" url="https://www.linkedin.com/in/ACoAAADjYaoBKCGNtZN_m0INLC4MnN9yFkTDUqY"
189
+ uid=e767 generic value="Julien Guézennec"
190
+ uid=e768 img "LinkedIn Verified"
191
+ uid=e770 time value="• 10:25 PM"
192
+ uid=e774 paragraph
193
+ uid=s73 StaticText "Je crois que j'ai trouvé mes tarifs ;)"
194
+ uid=e775 link "https://julienweb.fr/services-julienweb-fr/intervention-durgence-piratage-incident-de-securite/" url="https://julienweb.fr/services-julienweb-fr/intervention-durgence-piratage-incident-de-securite/"
195
+ uid=s74 StaticText "Aussi bientôt validé \""
196
+ uid=e776 link "https://www.cybermalveillance.gouv.fr/" url="https://www.cybermalveillance.gouv.fr/"
197
+ uid=s75 StaticText "\""
198
+ uid=e781 button "Click or press enter to display in the image preview"
199
+ uid=e782 generic value="Click or press enter to display in the image preview"
200
+ uid=e783 img
201
+ uid=e784 listitem
202
+ uid=e785 generic
203
+ uid=s76 unknown url="https://www.linkedin.com/in/ACoAAADjYaoBKCGNtZN_m0INLC4MnN9yFkTDUqY"
204
+ uid=e787 generic value="View Julien’s profile"
205
+ uid=s77 unknown
206
+ uid=e789 generic
207
+ uid=e790 generic
208
+ uid=e791 link "Julien Guézennec" url="https://www.linkedin.com/in/ACoAAADjYaoBKCGNtZN_m0INLC4MnN9yFkTDUqY"
209
+ uid=e792 generic value="Julien Guézennec"
210
+ uid=e793 img "LinkedIn Verified"
211
+ uid=e795 time value="• 10:27 PM"
212
+ uid=e799 paragraph value=":p"
213
+ uid=e804 button "Click or press enter to display in the image preview"
214
+ uid=e805 generic value="Click or press enter to display in the image preview"
215
+ uid=e806 img
216
+ uid=e807 listitem
217
+ uid=e808 time value="Apr 18"
218
+ uid=e809 generic value="Jerome Etienne sent the following messages at 1:17 PM"
219
+ uid=e810 generic
220
+ uid=e811 link "View Jerome’s profile Jerome Etienne" url="https://www.linkedin.com/in/ACoAAABV1ZYB_x7AQ_K0oe15LPzT32jClDzkqjQ"
221
+ uid=e812 generic value="View Jerome’s profile"
222
+ uid=e813 img "Jerome Etienne"
223
+ uid=e814 generic
224
+ uid=e815 generic
225
+ uid=e816 link "Jerome Etienne" url="https://www.linkedin.com/in/ACoAAABV1ZYB_x7AQ_K0oe15LPzT32jClDzkqjQ"
226
+ uid=e817 generic value="Jerome Etienne"
227
+ uid=e818 img "LinkedIn Verified"
228
+ uid=e820 time value="• 1:17 PM"
229
+ uid=e824 paragraph value="youpi!"
230
+ uid=e825 listitem
231
+ uid=e830 paragraph
232
+ uid=s78 StaticText "dit moi tu as whatapps ?"
233
+ uid=s79 StaticText "je passe pas si souvent que ca sur linkedin"
234
+ uid=e831 listitem
235
+ uid=e836 paragraph value="ca fluidifirai la communication :)"
236
+ uid=e837 listitem
237
+ uid=e838 generic
238
+ uid=e839 link "View Jerome’s profile Jerome Etienne" url="https://www.linkedin.com/in/ACoAAABV1ZYB_x7AQ_K0oe15LPzT32jClDzkqjQ"
239
+ uid=e840 generic value="View Jerome’s profile"
240
+ uid=e841 img "Jerome Etienne"
241
+ uid=e842 generic
242
+ uid=e843 generic
243
+ uid=e844 link "Jerome Etienne" url="https://www.linkedin.com/in/ACoAAABV1ZYB_x7AQ_K0oe15LPzT32jClDzkqjQ"
244
+ uid=e845 generic value="Jerome Etienne"
245
+ uid=e846 img "LinkedIn Verified"
246
+ uid=e848 time value="• 1:20 PM"
247
+ uid=e852 paragraph
248
+ uid=s80 StaticText "jai trouve sur ton site"
249
+ uid=s81 StaticText "je tai envoye un message :)"
250
+ uid=e854 img "Seen by Julien Guézennec at 2:04 PM."
251
+ uid=e855 listitem
252
+ uid=s82 listitem
package/listitem CHANGED
@@ -1,3 +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
+
1
5
  Expected ] at column 22:
2
6
  list[name=Conversation List]
3
7
  ^
package/package.json CHANGED
@@ -1,11 +1,13 @@
1
1
  {
2
2
  "name": "fastbrowser_cli",
3
- "version": "1.0.37",
3
+ "version": "1.0.40",
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.7"
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\"",