fastbrowser_cli 1.0.30 → 1.0.33

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 (56) hide show
  1. package/README.md +1 -2
  2. package/dist/fastbrowser_cli/fastbrowser_cli.js +11 -19
  3. package/dist/fastbrowser_cli/fastbrowser_cli.js.map +1 -1
  4. package/dist/fastbrowser_cli/libs/query-builder.d.ts +2 -0
  5. package/dist/fastbrowser_cli/libs/query-builder.d.ts.map +1 -1
  6. package/dist/fastbrowser_cli/libs/query-builder.js +4 -0
  7. package/dist/fastbrowser_cli/libs/query-builder.js.map +1 -1
  8. package/dist/fastbrowser_httpd/libs/tool-schemas.d.ts +2 -0
  9. package/dist/fastbrowser_httpd/libs/tool-schemas.d.ts.map +1 -1
  10. package/dist/fastbrowser_mcp/fastbrowser_mcp.d.ts.map +1 -1
  11. package/dist/fastbrowser_mcp/fastbrowser_mcp.js +147 -22
  12. package/dist/fastbrowser_mcp/fastbrowser_mcp.js.map +1 -1
  13. package/dist/fastbrowser_mcp/libs/mcp_my_client.d.ts.map +1 -1
  14. package/dist/fastbrowser_mcp/libs/mcp_my_client.js +8 -0
  15. package/dist/fastbrowser_mcp/libs/mcp_my_client.js.map +1 -1
  16. package/dist/fastbrowser_mcp/libs/mcp_target_helper.d.ts.map +1 -1
  17. package/dist/fastbrowser_mcp/libs/mcp_target_helper.js +15 -2
  18. package/dist/fastbrowser_mcp/libs/mcp_target_helper.js.map +1 -1
  19. package/dist/fastbrowser_mcp/libs/schemas.d.ts +4 -0
  20. package/dist/fastbrowser_mcp/libs/schemas.d.ts.map +1 -1
  21. package/dist/fastbrowser_mcp/libs/schemas.js +6 -0
  22. package/dist/fastbrowser_mcp/libs/schemas.js.map +1 -1
  23. package/dist/shared/logger.d.ts +86 -0
  24. package/dist/shared/logger.d.ts.map +1 -0
  25. package/dist/shared/logger.js +269 -0
  26. package/dist/shared/logger.js.map +1 -0
  27. package/docs/brainstorm_scrap_by_ai.md +1 -1
  28. package/docs/feature_support_cli.md +7 -8
  29. package/docs/target_tools/target_tools_chrome_devtools.md +963 -0
  30. package/docs/target_tools/target_tools_playwright.md +763 -0
  31. package/examples/linkedin_cli/linked_dm.sh +16 -0
  32. package/examples/linkedin_cli/linked_post.sh +13 -0
  33. package/examples/linkedin_cli/linkedin.snapshot.txt +1245 -0
  34. package/examples/todomvc/todomvc.a11y.txt +44 -0
  35. package/examples/todomvc/todomvc.sh +11 -0
  36. package/examples/wttj_cli/fastbrowser_helper.ts +39 -0
  37. package/examples/wttj_cli/wttf_job-original.a11y.txt +652 -0
  38. package/examples/wttj_cli/wttf_job.a11y.txt +317 -0
  39. package/examples/{welcometothejungle/wttj-job.ts → wttj_cli/wttj_job copy.ts } +60 -11
  40. package/examples/wttj_cli/wttj_job.ts +179 -0
  41. package/examples/wttj_cli/wttj_search.ts +162 -0
  42. package/package.json +10 -4
  43. package/skills/fastbrowser/SKILL.md +10 -11
  44. package/skills/fastbrowser-script/SKILL.md +4 -4
  45. package/src/fastbrowser_cli/fastbrowser_cli.ts +14 -25
  46. package/src/fastbrowser_cli/libs/query-builder.ts +6 -0
  47. package/src/fastbrowser_mcp/fastbrowser_mcp.ts +181 -26
  48. package/src/fastbrowser_mcp/libs/mcp_my_client.ts +17 -0
  49. package/src/fastbrowser_mcp/libs/mcp_target_helper.ts +15 -2
  50. package/src/fastbrowser_mcp/libs/schemas.ts +6 -0
  51. package/src/shared/logger.ts +317 -0
  52. package/test.a11y.txt +828 -0
  53. package/tests/query-builder.test.ts +51 -11
  54. package/examples/welcometothejungle/fastbrowser_helper.ts +0 -39
  55. package/examples/welcometothejungle/wttj-search.ts +0 -82
  56. /package/examples/{post-to-x.sh → twitter_cli/twitter_post.sh} +0 -0
@@ -0,0 +1,317 @@
1
+ uid=e1 generic active
2
+ uid=e3 generic
3
+ uid=e29 generic
4
+ uid=e39 generic
5
+ uid=e40 generic
6
+ uid=e41 generic
7
+ uid=e43 generic
8
+ uid=e45 button
9
+ uid=e47 figure
10
+ uid=e48 img "OZITEM"
11
+ uid=e49 link "OZITEM" url="/fr/companies/ozitem"
12
+ uid=e51 generic value="OZITEM"
13
+ uid=e52 heading "Technicien informatique H/F" level="2"
14
+ uid=e53 generic
15
+ uid=e56 generic
16
+ uid=e57 generic
17
+ uid=e58 img
18
+ uid=s6 StaticText "CDI"
19
+ uid=e60 generic
20
+ uid=e61 img
21
+ uid=e63 generic value="Issy-les-Moulineaux"
22
+ uid=e64 generic
23
+ uid=e65 img
24
+ uid=e67 generic value="Télétravail non autorisé"
25
+ uid=e69 img
26
+ uid=e71 generic
27
+ uid=e72 img
28
+ uid=e74 generic value="Salaire :"
29
+ uid=s7 StaticText "Non spécifié"
30
+ uid=e75 generic
31
+ uid=e76 img
32
+ uid=e78 generic value="Début :"
33
+ uid=e79 generic value="10 février 2026"
34
+ uid=e80 generic
35
+ uid=e81 img
36
+ uid=e83 generic value="Expérience :"
37
+ uid=s8 StaticText "> 2 ans"
38
+ uid=e84 generic
39
+ uid=e85 img
40
+ uid=e87 generic value="Éducation :"
41
+ uid=s9 StaticText "Bac +2"
42
+ uid=e88 generic
43
+ uid=e89 generic
44
+ uid=e90 link "Postuler" url="https://careers.flatchr.io/vacancy/gqayv9lorgon6lxe-technicien-vip-h-f/apply?partner=67"
45
+ uid=e91 generic value="Postuler"
46
+ uid=e92 img
47
+ uid=e94 button "Sauvegarder"
48
+ uid=e95 generic
49
+ uid=e96 img
50
+ uid=e98 img
51
+ uid=e108 generic value="Sauvegarder"
52
+ uid=e109 generic
53
+ uid=e110 generic
54
+ uid=e111 img
55
+ uid=e113 paragraph
56
+ uid=e114 time value="il y a 13 heures"
57
+ uid=e115 button "common.job-share-menu-trigger"
58
+ uid=e116 paragraph value="Partager"
59
+ uid=e117 img
60
+ uid=e119 generic
61
+ uid=e121 button
62
+ uid=e123 figure
63
+ uid=e124 img "OZITEM"
64
+ uid=e125 link "OZITEM" url="/fr/companies/ozitem"
65
+ uid=e126 generic value="OZITEM"
66
+ uid=e127 paragraph value="Cette offre vous tente ?"
67
+ uid=e128 generic
68
+ uid=e129 link "Postuler" url="https://careers.flatchr.io/vacancy/gqayv9lorgon6lxe-technicien-vip-h-f/apply?partner=67"
69
+ uid=e130 generic value="Postuler"
70
+ uid=e131 img
71
+ uid=e133 button "Sauvegarder"
72
+ uid=e134 generic
73
+ uid=e135 img
74
+ uid=e137 img
75
+ uid=e147 generic value="Sauvegarder"
76
+ uid=e148 generic
77
+ uid=e149 generic value="Questions et réponses sur l'offre"
78
+ uid=e151 button "Le télétravail est-il possible pour ce poste ?"
79
+ uid=e152 generic value="Le télétravail est-il possible pour ce poste ?"
80
+ uid=e154 img
81
+ uid=e157 button "Quel est le type de contrat pour ce poste ?"
82
+ uid=e158 generic value="Quel est le type de contrat pour ce poste ?"
83
+ uid=e160 img
84
+ uid=e163 button "Quelle est la date de début du contrat?"
85
+ uid=e164 generic value="Quelle est la date de début du contrat?"
86
+ uid=e166 img
87
+ uid=e169 generic
88
+ uid=e171 heading "Le poste" level="3"
89
+ uid=e172 generic value="Le poste"
90
+ uid=e173 generic
91
+ uid=e174 generic
92
+ uid=e175 heading "Descriptif du poste" level="4"
93
+ uid=e179 list
94
+ uid=e180 listitem value="Résoudre de manière proactive les problèmes techniques rencontrés par les utilisateurs VIP et proposer des solutions de contournement adaptées."
95
+ uid=e181 listitem value="Maintenir un haut niveau de connaissance sur les technologies Microsoft 365, Windows 11, macOS et les environnements mobiles Apple/Android."
96
+ uid=e182 listitem value="Utiliser et administrer ServiceNow pour le suivi des tickets et l’application des procédures internes."
97
+ uid=e183 listitem value="Exploiter Power Automate et PowerShell pour automatiser des tâches et améliorer l’efficacité du support."
98
+ uid=e184 listitem value="Gérer et sécuriser l’usage de Microsoft Copilot, former les VIP et optimiser leur productivité sur Microsoft 365."
99
+ uid=e185 listitem value="Administrer les comptes via Active Directory et Microsoft Entra."
100
+ uid=e186 listitem value="Assurer le support avancé de Teams, SharePoint, OneDrive et de l’ensemble de la suite Microsoft 365."
101
+ uid=e187 listitem value="Intervenir sur le hardware et garantir un diagnostic fiable des équipements."
102
+ uid=e188 listitem value="Gérer et maintenir les postes via Intune (UEM)."
103
+ uid=e189 separator
104
+ uid=e190 generic
105
+ uid=e191 heading "Profil recherché" level="4"
106
+ uid=e194 generic
107
+ uid=e195 list
108
+ uid=e196 listitem value="Solide expertise technique sur Windows 11, macOS, Microsoft 365 et les environnements mobiles."
109
+ uid=e197 listitem value="Maîtrise des outils d’automatisation (Power Automate, PowerShell, Zscaler)."
110
+ uid=e198 listitem value="Connaissance de ServiceNow et des procédures ITIL."
111
+ uid=e199 listitem value="Excellentes compétences en communication et sens du service VIP."
112
+ uid=e200 listitem value="Anglais niveau B2."
113
+ uid=e201 paragraph value="Ce poste est ouvert aux personnes disposant d’un droit au travail en France. Merci de préciser dans votre candidature si votre statut nécessite une autorisation de travail."
114
+ uid=e202 generic
115
+ uid=e204 heading "Envie d’en savoir plus ?" level="3"
116
+ uid=e205 generic value="Envie d’en savoir plus ?"
117
+ uid=e206 generic
118
+ uid=e207 button "modal-FullScreenVideo"
119
+ uid=e209 figure "Play Rencontrez Julie, Consultante ITSM"
120
+ uid=e210 generic
121
+ uid=e212 img "Play"
122
+ uid=e214 paragraph value="Rencontrez Julie, Consultante ITSM"
123
+ uid=e215 button "modal-FullScreenVideo"
124
+ uid=e217 figure "Play Rencontrez Julien, Responsable Opérationnel Dédié"
125
+ uid=e218 generic
126
+ uid=e220 img "Play"
127
+ uid=e222 paragraph value="Rencontrez Julien, Responsable Opérationnel Dédié"
128
+ uid=e223 complementary
129
+ uid=e224 generic
130
+ uid=e225 generic
131
+ uid=e226 button "modal-FullScreenImage"
132
+ uid=e227 img "OZITEM"
133
+ uid=e228 button "modal-FullScreenImage"
134
+ uid=e229 img "OZITEM"
135
+ uid=e230 button "modal-FullScreenImage"
136
+ uid=e231 img "OZITEM"
137
+ uid=e232 button "modal-FullScreenImage"
138
+ uid=e233 img "OZITEM"
139
+ uid=e234 generic
140
+ uid=e235 heading "Découvrez l'entreprise" level="5"
141
+ uid=e236 paragraph value="Explorez la vitrine de l’entreprise ou suivez-la pour savoir si elle vous correspond vraiment !"
142
+ uid=e237 generic
143
+ uid=e238 link "Explorer l’entreprise" url="/fr/companies/ozitem"
144
+ uid=e239 generic value="Explorer l’entreprise"
145
+ uid=e240 img
146
+ uid=e242 button "Suivre"
147
+ uid=e243 generic
148
+ uid=e245 generic value="Ils sont sociables"
149
+ uid=e246 generic
150
+ uid=e247 link "Facebook" url="https://www.facebook.com/Groupe-Ozitem-214935588593562"
151
+ uid=e248 img
152
+ uid=e250 link "Linkedin" url="https://www.linkedin.com/company/43074"
153
+ uid=e251 img
154
+ uid=e253 link "Twitter" url="https://www.twitter.com/GroupeOZITEM"
155
+ uid=e254 img
156
+ uid=e256 link "Youtube" url="https://www.youtube.com/groupeozitem"
157
+ uid=e257 img
158
+ uid=e260 generic
159
+ uid=e262 heading "L'entreprise" level="3"
160
+ uid=e263 generic value="L'entreprise"
161
+ uid=e264 generic
162
+ uid=e265 generic
163
+ uid=e267 button
164
+ uid=e269 figure
165
+ uid=e270 img "OZITEM"
166
+ uid=e271 link "OZITEM" url="/fr/companies/ozitem"
167
+ uid=e273 generic value="OZITEM"
168
+ uid=e274 generic
169
+ uid=e275 generic
170
+ uid=e276 img
171
+ uid=e278 generic value="IT / Digital, SaaS / Cloud Services"
172
+ uid=e279 generic
173
+ uid=e280 img
174
+ uid=e282 generic value="400 collaborateurs"
175
+ uid=e283 generic
176
+ uid=e284 img
177
+ uid=e286 generic value="Créée en 1990"
178
+ uid=e287 generic
179
+ uid=e288 img
180
+ uid=e290 generic value="Âge moyen : 31 ans"
181
+ uid=e291 generic
182
+ uid=e292 img
183
+ uid=e294 generic value="Chiffre d'affaires : 46 M€"
184
+ uid=e295 generic
185
+ uid=e296 img
186
+ uid=e298 generic value="20%"
187
+ uid=e299 generic
188
+ uid=e300 img
189
+ uid=e302 generic value="80%"
190
+ uid=e303 list
191
+ uid=e304 listitem
192
+ uid=e305 link "Voir le site" url="http://groupeozitem.com"
193
+ uid=e306 generic value="Voir le site"
194
+ uid=e307 img
195
+ uid=s10 StaticText "•"
196
+ uid=e309 listitem
197
+ uid=e310 link "Voir toutes les offres 19" url="/fr/companies/ozitem/jobs"
198
+ uid=e311 generic value="Voir toutes les offres"
199
+ uid=e312 generic value="19"
200
+ uid=e313 separator
201
+ uid=e314 generic
202
+ uid=e315 heading "Qui sont-ils ?" level="4"
203
+ uid=e316 generic
204
+ uid=e318 generic
205
+ uid=e319 paragraph
206
+ uid=s11 StaticText "Chez Ozitem, nous accompagnons les entreprises dans leur transformation digitale, notamment au travers de notre expertise sur les solutions d’infrastructures & de cloud 💡🚀"
207
+ uid=s12 StaticText "Entreprise de taille humaine et pérenne depuis plus de 30 ans, l’entreprise affiche une croissance régulière depuis notre création et un chiffre d’affaires de 46 Millions d’euros en 2024."
208
+ uid=s13 StaticText "Ce sont 400 Ozitémiens, ingénieurs et consultants qui évoluent sur trois sites en France : Levallois (92), notre siège-social, Toulouse (31) et Lyon (69). 👨‍ 💻"
209
+ uid=s14 StaticText "Avantages :"
210
+ uid=e321 list
211
+ uid=e322 listitem value="RTT"
212
+ uid=e323 listitem value="Tickets restaurants"
213
+ uid=e324 listitem value="Participation"
214
+ uid=e325 listitem value="CSE"
215
+ uid=e326 listitem value="75% transport"
216
+ uid=e328 button "Voir plus"
217
+ uid=e329 generic value="Voir plus"
218
+ uid=e330 img
219
+ uid=e332 separator
220
+ uid=e333 generic
221
+ uid=e334 generic value="Les dimensions culturelles de l’entreprise"
222
+ uid=e335 generic
223
+ uid=e336 generic
224
+ uid=e338 img "illustration"
225
+ uid=e339 generic
226
+ uid=e340 paragraph value="Méthode de travail"
227
+ uid=e341 generic value="Constance"
228
+ uid=e342 generic
229
+ uid=e344 img "illustration"
230
+ uid=e345 generic
231
+ uid=e346 paragraph value="Hiérarchie"
232
+ uid=e347 generic value="Horizontalité"
233
+ uid=e348 generic
234
+ uid=e350 img "illustration"
235
+ uid=e351 generic
236
+ uid=e352 paragraph value="Innovation"
237
+ uid=e353 generic value="Continuité"
238
+ uid=e354 generic
239
+ uid=e356 img "illustration"
240
+ uid=e357 generic
241
+ uid=e358 paragraph value="Philosophie"
242
+ uid=e359 generic value="Formalisme"
243
+ uid=e361 button "Voir le bilan complet"
244
+ uid=e362 generic value="Voir le bilan complet"
245
+ uid=e363 separator
246
+ uid=e364 generic
247
+ uid=e365 heading "Les avantages salariés" level="4"
248
+ uid=e366 list
249
+ uid=e367 listitem value="RTT / Jour de repos"
250
+ uid=e368 listitem value="Entre 1-2 jours de télétravail"
251
+ uid=e369 listitem value="Prime de cooptation"
252
+ uid=e370 listitem value="Afterworks, Déjeuners d'équipe, etc."
253
+ uid=e371 listitem value="Remboursement au delà des 50%"
254
+ uid=e372 listitem value="Participation"
255
+ uid=e373 generic
256
+ uid=e374 button "Voir tous les avantages"
257
+ uid=e375 generic value="Voir tous les avantages"
258
+ uid=e376 generic value="16"
259
+ uid=e377 separator
260
+ uid=e378 generic
261
+ uid=e379 paragraph value="Index de l’égalité professionnelle"
262
+ uid=e380 paragraph value="Femmes-hommes"
263
+ uid=e381 generic
264
+ uid=e382 paragraph value="93"
265
+ uid=e383 paragraph value="sur 100 points"
266
+ uid=e388 list
267
+ uid=e389 listitem
268
+ uid=e390 paragraph value="Écart rémunération"
269
+ uid=e391 paragraph value="38/40"
270
+ uid=e392 img
271
+ uid=e394 listitem
272
+ uid=e395 paragraph value="Écart taux d'augmentation (hors promotion)"
273
+ uid=e396 paragraph value="20/20"
274
+ uid=e397 img
275
+ uid=e399 listitem
276
+ uid=e400 paragraph value="Écart taux de promotion"
277
+ uid=e401 paragraph value="15/15"
278
+ uid=e402 img
279
+ uid=e404 listitem
280
+ uid=e405 paragraph value="Retour congés maternité"
281
+ uid=e406 paragraph value="15/15"
282
+ uid=e407 img
283
+ uid=e409 listitem
284
+ uid=e410 paragraph value="Hautes rémunérations"
285
+ uid=e411 paragraph value="5/10"
286
+ uid=e412 img
287
+ uid=e414 generic
288
+ uid=e416 img
289
+ uid=e420 generic
290
+ uid=s15 StaticText "Données issues de la"
291
+ uid=e421 link "déclaration obligatoire" url="https://egapro.travail.gouv.fr/consulter-index"
292
+ uid=s16 StaticText "faite par les entreprises de plus de 50 salaires, auprès du ministère du travail, qui permet de mettre en lumière les disparités salariales femmes/hommes."
293
+ uid=e422 separator
294
+ uid=e423 generic
295
+ uid=e424 heading "Engagements" level="4"
296
+ uid=e426 generic
297
+ uid=e428 generic
298
+ uid=e429 img "Certification ISO 14001"
299
+ uid=e430 generic value="Certification ISO 14001"
300
+ uid=e432 generic
301
+ uid=e433 img "Certification ISO 9001"
302
+ uid=e434 generic value="Certification ISO 9001"
303
+ uid=e435 separator
304
+ uid=e436 generic
305
+ uid=e437 heading "Le lieu de travail" level="4"
306
+ uid=e438 link "Issy-les-Moulineaux, Ile-de-France, France" url="http://maps.google.com/?q=Issy-les-Moulineaux, Ile-de-France, France"
307
+ uid=e439 generic
308
+ uid=e440 generic value="Issy-les-Moulineaux, Ile-de-France, France"
309
+ uid=e441 img
310
+ uid=e443 generic
311
+ uid=e444 generic
312
+ uid=e446 img
313
+ uid=e448 heading "Besoin de plus d’infos ?" level="5"
314
+ uid=e449 paragraph value="Vie d’entreprise, ambiance, réalisations... On a encore plein de choses à vous dire !"
315
+ uid=e450 link "Découvrir" url="/fr/companies/ozitem/culture-1"
316
+ uid=e451 generic value="Découvrir"
317
+ uid=e452 img
@@ -1,8 +1,13 @@
1
1
  #!/usr/bin/env npx tsx
2
2
  // Usage: npx tsx extract-wttj-job.ts <job-url>
3
3
 
4
+ import * as Commander from 'commander';
4
5
  import { FastBrowserHelper } from "./fastbrowser_helper.js";
5
6
 
7
+ ///////////////////////////////////////////////////////////////////////////////
8
+ ///////////////////////////////////////////////////////////////////////////////
9
+ //
10
+ ///////////////////////////////////////////////////////////////////////////////
6
11
  ///////////////////////////////////////////////////////////////////////////////
7
12
 
8
13
  const ALL_STOPS = [
@@ -21,6 +26,10 @@ const ALL_STOPS = [
21
26
  'What we offer',
22
27
  ];
23
28
 
29
+ ///////////////////////////////////////////////////////////////////////////////
30
+ ///////////////////////////////////////////////////////////////////////////////
31
+ //
32
+ ///////////////////////////////////////////////////////////////////////////////
24
33
  ///////////////////////////////////////////////////////////////////////////////
25
34
 
26
35
  class SnapshotParser {
@@ -109,14 +118,20 @@ class WttjExtractor {
109
118
  console.log(lines.join('\n'));
110
119
  }
111
120
 
121
+ ///////////////////////////////////////////////////////////////////////////////
122
+ ///////////////////////////////////////////////////////////////////////////////
123
+ //
124
+ ///////////////////////////////////////////////////////////////////////////////
125
+ ///////////////////////////////////////////////////////////////////////////////
126
+
112
127
  static async run(jobUrl: string): Promise<void> {
113
- FastBrowserHelper.navigatePage(jobUrl);
128
+ await FastBrowserHelper.navigatePage(jobUrl);
114
129
 
115
130
  console.log('=== TITRE ===');
116
- const selectorOutput = FastBrowserHelper.querySelectors('heading[level="2"]', false);
131
+ const selectorOutput = await FastBrowserHelper.querySelectors('heading[level="2"]', false);
117
132
  console.log(WttjExtractor.extractTitle(selectorOutput));
118
133
 
119
- const snapshot = FastBrowserHelper.takeSnapshot();
134
+ const snapshot = await FastBrowserHelper.takeSnapshot();
120
135
  const parser = new SnapshotParser(snapshot);
121
136
 
122
137
  let descriptif = parser.extractAfterHeading('Descriptif du poste');
@@ -146,14 +161,48 @@ class WttjExtractor {
146
161
  }
147
162
 
148
163
  ///////////////////////////////////////////////////////////////////////////////
164
+ ///////////////////////////////////////////////////////////////////////////////
165
+ //
166
+ ///////////////////////////////////////////////////////////////////////////////
167
+ ///////////////////////////////////////////////////////////////////////////////
168
+
169
+ async function main() {
170
+ ///////////////////////////////////////////////////////////////////////////////
171
+ ///////////////////////////////////////////////////////////////////////////////
172
+ //
173
+ ///////////////////////////////////////////////////////////////////////////////
174
+ ///////////////////////////////////////////////////////////////////////////////
175
+
176
+ const program = new Commander.Command();
177
+ program
178
+ .argument('url', 'job page url')
179
+ .addOption(new Commander.Option('-f, --format <format>', 'output format: json or markdown').choices(['json', 'markdown']).default('markdown'))
180
+ .parse();
181
+
182
+ type CliOptions = {
183
+ format: 'json' | 'markdown';
184
+ };
185
+ const options: CliOptions = program.opts<CliOptions>();
186
+
187
+ ///////////////////////////////////////////////////////////////////////////////
188
+ ///////////////////////////////////////////////////////////////////////////////
189
+ //
190
+ ///////////////////////////////////////////////////////////////////////////////
191
+ /////////////////////////////////////////// ////////////////////////////////////
192
+
193
+ const jobUrl = program.args[0];
194
+ if (jobUrl === undefined || jobUrl === '') {
195
+ console.error('Usage: npx tsx wttj_job.ts <job-url>');
196
+ process.exit(1);
197
+ }
149
198
 
150
- const jobUrl = process.argv[2];
151
- if (jobUrl === undefined || jobUrl === '') {
152
- console.error('Usage: npx tsx extract-wttj-job.ts <job-url>');
153
- process.exit(1);
199
+ await WttjExtractor.run(jobUrl)
154
200
  }
155
201
 
156
- WttjExtractor.run(jobUrl).catch((err: unknown) => {
157
- console.error('Error:', err);
158
- process.exit(1);
159
- });
202
+ ///////////////////////////////////////////////////////////////////////////////
203
+ ///////////////////////////////////////////////////////////////////////////////
204
+ //
205
+ ///////////////////////////////////////////////////////////////////////////////
206
+ ///////////////////////////////////////////////////////////////////////////////
207
+
208
+ void main();
@@ -0,0 +1,179 @@
1
+ #!/usr/bin/env npx tsx
2
+ // Usage: npx tsx extract-wttj-job.ts <job-url>
3
+
4
+ import * as Commander from 'commander';
5
+ import { FastBrowserHelper } from "./fastbrowser_helper.js";
6
+ import { A11yTree, A11yQuery, AxNode, A11yDisplay } from 'a11y_parse';
7
+ import { get } from 'node:http';
8
+
9
+ type WttjJobOffer = {
10
+ url: string;
11
+ title: string;
12
+
13
+ salaryStr?: string;
14
+ startDateStr?: string;
15
+ experienceStr?: string;
16
+ educationStr?: string;
17
+
18
+ }
19
+
20
+ ///////////////////////////////////////////////////////////////////////////////
21
+ ///////////////////////////////////////////////////////////////////////////////
22
+ //
23
+ ///////////////////////////////////////////////////////////////////////////////
24
+ ///////////////////////////////////////////////////////////////////////////////
25
+
26
+ class WttjExtractor {
27
+ static async run(jobUrl: string): Promise<void> {
28
+ // Go to the job page
29
+ await FastBrowserHelper.navigatePage(jobUrl);
30
+
31
+ // Take an accessibility snapshot of the page and parse it into an AxNode tree
32
+ const a11ySnapshot = await FastBrowserHelper.takeSnapshot();
33
+ const axTree = A11yTree.parse(a11ySnapshot);
34
+
35
+ // Remove the banner node from the tree and print the modified tree
36
+ const axNodeBanner = A11yQuery.querySelector(axTree, 'banner');
37
+ if (axNodeBanner === undefined) throw new Error('No banner node found');
38
+ A11yTree.remove(axTree, axNodeBanner);
39
+
40
+
41
+ ///////////////////////////////////////////////////////////////////////////////
42
+ ///////////////////////////////////////////////////////////////////////////////
43
+ //
44
+ ///////////////////////////////////////////////////////////////////////////////
45
+ ///////////////////////////////////////////////////////////////////////////////
46
+
47
+ console.log('Accessibility Snapshot without banner:');
48
+ console.log(A11yDisplay.stringifyTree(axTree));
49
+
50
+ ///////////////////////////////////////////////////////////////////////////////
51
+ ///////////////////////////////////////////////////////////////////////////////
52
+ //
53
+ ///////////////////////////////////////////////////////////////////////////////
54
+ ///////////////////////////////////////////////////////////////////////////////
55
+
56
+ const jobOffer: Partial<WttjJobOffer> = {}
57
+ // Set the job URL
58
+ jobOffer.url = jobUrl;
59
+
60
+ ///////////////////////////////////////////////////////////////////////////////
61
+ ///////////////////////////////////////////////////////////////////////////////
62
+ // jobOffer.title
63
+ ///////////////////////////////////////////////////////////////////////////////
64
+ ///////////////////////////////////////////////////////////////////////////////
65
+
66
+ if (true) {
67
+ // debugger
68
+ // find the axNode corresponding to the job title 'heading[level="2"]'
69
+ const axSelector = 'heading[level="2"]';
70
+ const axNodeHeading = A11yQuery.querySelector(axTree, axSelector);
71
+ if (axNodeHeading === undefined) throw new Error(`No jobTitle found - axSelector=${axSelector}`);
72
+ if (axNodeHeading.name === undefined) throw new Error(`Job title node has no name - axSelector=${axSelector}`);
73
+ // Set the job title
74
+ jobOffer.title = axNodeHeading.name;
75
+ }
76
+
77
+ ///////////////////////////////////////////////////////////////////////////////
78
+ ///////////////////////////////////////////////////////////////////////////////
79
+ //
80
+ ///////////////////////////////////////////////////////////////////////////////
81
+ ///////////////////////////////////////////////////////////////////////////////
82
+
83
+ async function getJobTagKeyValueHelper(axTree: AxNode, jobTagName: string): Promise<string | undefined> {
84
+ // find the axNode corresponding to the job tag name 'generic[value="Salaire :"]'
85
+ const axSelector = `generic[value="${jobTagName}"] + StaticText`;
86
+ const axNode = A11yQuery.querySelector(axTree, axSelector);
87
+ if (axNode !== undefined && axNode.name !== undefined) {
88
+ return axNode.name;
89
+ }
90
+ return undefined;
91
+ }
92
+
93
+ if (true) {
94
+ const valueStr: string | undefined = await getJobTagKeyValueHelper(axTree, 'Salaire :');
95
+ if (valueStr !== undefined) {
96
+ jobOffer.salaryStr = valueStr;
97
+ } else {
98
+ console.warn('Salary not found');
99
+ }
100
+ }
101
+
102
+ if (true) {
103
+ const valueStr: string | undefined = await getJobTagKeyValueHelper(axTree, 'Début :');
104
+ if (valueStr !== undefined) {
105
+ jobOffer.startDateStr = valueStr;
106
+ } else {
107
+ console.warn('Start date not found');
108
+ }
109
+ }
110
+
111
+
112
+ if (true) {
113
+ const valueStr: string | undefined = await getJobTagKeyValueHelper(axTree, 'Expérience :');
114
+ if (valueStr !== undefined) {
115
+ jobOffer.experienceStr = valueStr;
116
+ } else {
117
+ console.warn('Experience not found');
118
+ }
119
+ }
120
+
121
+ if (true) {
122
+ const valueStr: string | undefined = await getJobTagKeyValueHelper(axTree, 'Éducation :');
123
+ if (valueStr !== undefined) {
124
+ jobOffer.educationStr = valueStr;
125
+ } else {
126
+ console.warn('Education not found');
127
+ }
128
+ }
129
+
130
+ console.log('Extracted job offer:', jobOffer);
131
+ }
132
+ }
133
+
134
+ ///////////////////////////////////////////////////////////////////////////////
135
+ ///////////////////////////////////////////////////////////////////////////////
136
+ //
137
+ ///////////////////////////////////////////////////////////////////////////////
138
+ ///////////////////////////////////////////////////////////////////////////////
139
+
140
+ async function main() {
141
+ ///////////////////////////////////////////////////////////////////////////////
142
+ ///////////////////////////////////////////////////////////////////////////////
143
+ //
144
+ ///////////////////////////////////////////////////////////////////////////////
145
+ ///////////////////////////////////////////////////////////////////////////////
146
+
147
+ const program = new Commander.Command();
148
+ program
149
+ .argument('url', 'job page url')
150
+ .addOption(new Commander.Option('-f, --format <format>', 'output format: json or markdown').choices(['json', 'markdown']).default('markdown'))
151
+ .parse();
152
+
153
+ type CliOptions = {
154
+ format: 'json' | 'markdown';
155
+ };
156
+ const options: CliOptions = program.opts<CliOptions>();
157
+
158
+ ///////////////////////////////////////////////////////////////////////////////
159
+ ///////////////////////////////////////////////////////////////////////////////
160
+ //
161
+ ///////////////////////////////////////////////////////////////////////////////
162
+ /////////////////////////////////////////// ////////////////////////////////////
163
+
164
+ const jobUrl = program.args[0];
165
+ if (jobUrl === undefined || jobUrl === '') {
166
+ console.error('Usage: npx tsx wttj_job.ts <job-url>');
167
+ process.exit(1);
168
+ }
169
+
170
+ await WttjExtractor.run(jobUrl)
171
+ }
172
+
173
+ ///////////////////////////////////////////////////////////////////////////////
174
+ ///////////////////////////////////////////////////////////////////////////////
175
+ //
176
+ ///////////////////////////////////////////////////////////////////////////////
177
+ ///////////////////////////////////////////////////////////////////////////////
178
+
179
+ void main();