fastbrowser_cli 1.0.31 → 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 +9 -3
  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
@@ -18,7 +18,7 @@ describe('QueryBuilder.buildQuerySelectorsBody', () => {
18
18
  selector: ['button'],
19
19
  });
20
20
  assert.deepEqual(body, {
21
- selectors: [{ selector: 'button', limit: 0, withAncestors: true }],
21
+ selectors: [{ selector: 'button', limit: 0, withAncestors: true, withChildren: false }],
22
22
  });
23
23
  });
24
24
 
@@ -29,8 +29,8 @@ describe('QueryBuilder.buildQuerySelectorsBody', () => {
29
29
  });
30
30
  assert.deepEqual(body, {
31
31
  selectors: [
32
- { selector: 'button', limit: 5, withAncestors: true },
33
- { selector: 'link', limit: 5, withAncestors: true },
32
+ { selector: 'button', limit: 5, withAncestors: true, withChildren: false },
33
+ { selector: 'link', limit: 5, withAncestors: true, withChildren: false },
34
34
  ],
35
35
  });
36
36
  });
@@ -50,6 +50,31 @@ describe('QueryBuilder.buildQuerySelectorsBody', () => {
50
50
  assert.equal(body.selectors[0].withAncestors, true);
51
51
  });
52
52
 
53
+ it('treats omitted withChildren as false (default)', () => {
54
+ const body = QueryBuilder.buildQuerySelectorsBody({
55
+ selector: ['button'],
56
+ });
57
+ assert.equal(body.selectors[0].withChildren, false);
58
+ });
59
+
60
+ it('honors --with-children (withChildren === true)', () => {
61
+ const body = QueryBuilder.buildQuerySelectorsBody({
62
+ selector: ['button'],
63
+ withChildren: true,
64
+ });
65
+ assert.equal(body.selectors[0].withChildren, true);
66
+ });
67
+
68
+ it('combines withAncestors and withChildren independently', () => {
69
+ const body = QueryBuilder.buildQuerySelectorsBody({
70
+ selector: ['button'],
71
+ withAncestors: false,
72
+ withChildren: true,
73
+ });
74
+ assert.equal(body.selectors[0].withAncestors, false);
75
+ assert.equal(body.selectors[0].withChildren, true);
76
+ });
77
+
53
78
  it('parses --limit as an integer', () => {
54
79
  const body = QueryBuilder.buildQuerySelectorsBody({
55
80
  selector: ['button'],
@@ -69,20 +94,20 @@ describe('QueryBuilder.buildQuerySelectorsBody', () => {
69
94
  describe('--selectors-json input', () => {
70
95
  it('parses a JSON array and uses it verbatim', () => {
71
96
  const body = QueryBuilder.buildQuerySelectorsBody({
72
- selectorsJson: '[{"selector":"button","limit":3,"withAncestors":false}]',
97
+ selectorsJson: '[{"selector":"button","limit":3,"withAncestors":false,"withChildren":true}]',
73
98
  });
74
99
  assert.deepEqual(body, {
75
- selectors: [{ selector: 'button', limit: 3, withAncestors: false }],
100
+ selectors: [{ selector: 'button', limit: 3, withAncestors: false, withChildren: true }],
76
101
  });
77
102
  });
78
103
 
79
104
  it('takes precedence over --selector when both are given', () => {
80
105
  const body = QueryBuilder.buildQuerySelectorsBody({
81
106
  selector: ['ignored'],
82
- selectorsJson: '[{"selector":"button","limit":1,"withAncestors":true}]',
107
+ selectorsJson: '[{"selector":"button","limit":1,"withAncestors":true,"withChildren":false}]',
83
108
  });
84
109
  assert.deepEqual(body.selectors, [
85
- { selector: 'button', limit: 1, withAncestors: true },
110
+ { selector: 'button', limit: 1, withAncestors: true, withChildren: false },
86
111
  ]);
87
112
  });
88
113
 
@@ -135,12 +160,12 @@ describe('QueryBuilder.buildQuerySelectorsBody', () => {
135
160
 
136
161
  describe('QueryBuilder.buildQuerySelectorFirstBody', () => {
137
162
  describe('--selector list', () => {
138
- it('builds a body with default withAncestors=true', () => {
163
+ it('builds a body with default withAncestors=true and withChildren=false', () => {
139
164
  const body = QueryBuilder.buildQuerySelectorFirstBody({
140
165
  selector: ['button'],
141
166
  });
142
167
  assert.deepEqual(body, {
143
- selectors: [{ selector: 'button', withAncestors: true }],
168
+ selectors: [{ selector: 'button', withAncestors: true, withChildren: false }],
144
169
  });
145
170
  });
146
171
 
@@ -152,6 +177,21 @@ describe('QueryBuilder.buildQuerySelectorFirstBody', () => {
152
177
  assert.equal(body.selectors[0].withAncestors, false);
153
178
  });
154
179
 
180
+ it('honors --with-children (withChildren === true)', () => {
181
+ const body = QueryBuilder.buildQuerySelectorFirstBody({
182
+ selector: ['button'],
183
+ withChildren: true,
184
+ });
185
+ assert.equal(body.selectors[0].withChildren, true);
186
+ });
187
+
188
+ it('treats omitted withChildren as false (default)', () => {
189
+ const body = QueryBuilder.buildQuerySelectorFirstBody({
190
+ selector: ['button'],
191
+ });
192
+ assert.equal(body.selectors[0].withChildren, false);
193
+ });
194
+
155
195
  it('builds a multi-selector body preserving order', () => {
156
196
  const body = QueryBuilder.buildQuerySelectorFirstBody({
157
197
  selector: ['a', 'b', 'c'],
@@ -163,10 +203,10 @@ describe('QueryBuilder.buildQuerySelectorFirstBody', () => {
163
203
  describe('--selectors-json input', () => {
164
204
  it('parses a JSON array verbatim', () => {
165
205
  const body = QueryBuilder.buildQuerySelectorFirstBody({
166
- selectorsJson: '[{"selector":"button","withAncestors":false}]',
206
+ selectorsJson: '[{"selector":"button","withAncestors":false,"withChildren":true}]',
167
207
  });
168
208
  assert.deepEqual(body, {
169
- selectors: [{ selector: 'button', withAncestors: false }],
209
+ selectors: [{ selector: 'button', withAncestors: false, withChildren: true }],
170
210
  });
171
211
  });
172
212
 
@@ -1,39 +0,0 @@
1
- import { execSync } from 'node:child_process';
2
-
3
- export class FastBrowserHelper {
4
- static run(command: string): string {
5
- const fullCommand = `npx fastbrowser_cli ${command}`;
6
- console.log(`Running command: ${fullCommand}`);
7
- return execSync(fullCommand, { encoding: 'utf8' });
8
- }
9
-
10
- static navigatePage(url: string): void {
11
- FastBrowserHelper.run(`navigate_page --url '${url}'`);
12
- }
13
-
14
- static fillForm(selector: string, value: string): void {
15
- FastBrowserHelper.run(`fill_form --selector '${selector}' --value '${value}'`);
16
- }
17
-
18
- static pressKeys(keys: string): void {
19
- FastBrowserHelper.run(`press_keys --keys '${keys}'`);
20
- }
21
-
22
- static click(selector: string): void {
23
- FastBrowserHelper.run(`click -s '${selector}'`);
24
- }
25
-
26
- static querySelectorsAll(selector: string, limit: number): string {
27
- return FastBrowserHelper.run(`query_selectors_all --selector '${selector}' --limit ${limit}`);
28
- }
29
-
30
- static takeSnapshot(): string {
31
- return FastBrowserHelper.run('take_snapshot');
32
- }
33
-
34
- static querySelectors(selector: string, withAncestors = true): string {
35
- const flag = withAncestors === false ? ' --no-with-ancestors' : '';
36
- return FastBrowserHelper.run(`query_selectors --selector '${selector}'${flag}`);
37
- }
38
-
39
- }
@@ -1,82 +0,0 @@
1
- #!/usr/bin/env npx tsx
2
-
3
- // npm imports
4
- import { Command, Option } from 'commander';
5
- import { FastBrowserHelper } from './fastbrowser_helper.js';
6
-
7
- ///////////////////////////////////////////////////////////////////////////////
8
- ///////////////////////////////////////////////////////////////////////////////
9
- //
10
- ///////////////////////////////////////////////////////////////////////////////
11
- ///////////////////////////////////////////////////////////////////////////////
12
-
13
- class WttjSearch {
14
- static parseJobTitles(output: string): Array<{ title: string; url: string; }> {
15
- const results: Array<{ title: string; url: string; }> = [];
16
- const headingRe = /heading "(.+?)" level/g;
17
- const urlRe = /url="([^"]+)"/;
18
- for (const line of output.split('\n')) {
19
- const headingMatch = headingRe.exec(line);
20
- if (headingMatch === null) continue;
21
- const urlMatch = urlRe.exec(line);
22
- results.push({
23
- title: headingMatch[1],
24
- url: urlMatch !== null ? urlMatch[1] : '',
25
- });
26
- headingRe.lastIndex = 0;
27
- }
28
- return results;
29
- }
30
-
31
- static outputJson(jobs: Array<{ title: string; url: string; }>): void {
32
- console.log(JSON.stringify(jobs, null, 2));
33
- }
34
-
35
- static outputMarkdown(jobs: Array<{ title: string; url: string; }>): void {
36
- console.log('| # | Title | URL |');
37
- console.log('|---|-------|-----|');
38
- for (const [i, job] of jobs.entries()) {
39
- const url = job.url !== '' ? `[link](${job.url})` : '';
40
- console.log(`| ${i + 1} | ${job.title} | ${url} |`);
41
- }
42
- }
43
-
44
- static run(query: string, limit: number = 1, offset: number = 1, format: 'json' | 'markdown' = 'markdown'): void {
45
- FastBrowserHelper.navigatePage('https://www.welcometothejungle.com/fr');
46
- FastBrowserHelper.fillForm('combobox[name*="intitulé de poste"]', query);
47
- FastBrowserHelper.pressKeys('Enter');
48
-
49
- let allJobs: Array<{ title: string; url: string; }> = [];
50
-
51
- for (let page = offset; page < offset + limit; page++) {
52
- if (page > 1) {
53
- FastBrowserHelper.click(`link[name="${page}"]`);
54
- }
55
- const output = FastBrowserHelper.querySelectorsAll('heading[level="2"]', 30);
56
- allJobs = allJobs.concat(WttjSearch.parseJobTitles(output));
57
- }
58
-
59
- if (format === 'json') {
60
- WttjSearch.outputJson(allJobs);
61
- } else {
62
- WttjSearch.outputMarkdown(allJobs);
63
- }
64
- }
65
- }
66
-
67
- ///////////////////////////////////////////////////////////////////////////////
68
-
69
- const program = new Command();
70
- program
71
- .argument('[query]', 'job search query', 'machine learning')
72
- .option('-l, --limit <number>', 'number of pages to fetch', '1')
73
- .option('-o, --offset <number>', 'first page to scrape', '1')
74
- .addOption(new Option('-f, --format <format>', 'output format: json or markdown').choices(['json', 'markdown']).default('markdown'))
75
- .parse();
76
-
77
- const opts = program.opts();
78
- const query = program.args[0] ?? 'machine learning';
79
- const limit = Number(opts['limit']);
80
- const offset = Number(opts['offset']);
81
- const format: 'json' | 'markdown' = opts['format'] === 'json' ? 'json' : 'markdown';
82
- WttjSearch.run(query, limit, offset, format);