fastbrowser_cli 1.0.22 → 1.0.28

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 (35) hide show
  1. package/README.md +55 -10
  2. package/dist/fastbrowser_cli/fastbrowser_cli.js +40 -70
  3. package/dist/fastbrowser_cli/fastbrowser_cli.js.map +1 -1
  4. package/dist/fastbrowser_cli/libs/query-builder.d.ts +17 -0
  5. package/dist/fastbrowser_cli/libs/query-builder.d.ts.map +1 -0
  6. package/dist/fastbrowser_cli/libs/query-builder.js +58 -0
  7. package/dist/fastbrowser_cli/libs/query-builder.js.map +1 -0
  8. package/dist/fastbrowser_cli/libs/server-manager.d.ts +8 -3
  9. package/dist/fastbrowser_cli/libs/server-manager.d.ts.map +1 -1
  10. package/dist/fastbrowser_cli/libs/server-manager.js +30 -15
  11. package/dist/fastbrowser_cli/libs/server-manager.js.map +1 -1
  12. package/dist/fastbrowser_httpd/fastbrowser_httpd.js +9 -6
  13. package/dist/fastbrowser_httpd/fastbrowser_httpd.js.map +1 -1
  14. package/dist/fastbrowser_httpd/libs/routes.d.ts +2 -1
  15. package/dist/fastbrowser_httpd/libs/routes.d.ts.map +1 -1
  16. package/dist/fastbrowser_httpd/libs/routes.js +2 -2
  17. package/dist/fastbrowser_httpd/libs/routes.js.map +1 -1
  18. package/docs/articles/ai_workflow_to_shell_scripts.md +74 -16
  19. package/docs/articles/ai_workflow_to_shell_scripts.notes.md +23 -0
  20. package/docs/articles/ai_workflow_to_shell_scripts.outline.md +64 -0
  21. package/docs/publishing_workflow.md +136 -0
  22. package/examples/post-to-x.sh +3 -0
  23. package/examples/welcometothejungle/fastbrowser_helper.ts +39 -0
  24. package/examples/welcometothejungle/wttj-job.ts +132 -153
  25. package/examples/welcometothejungle/wttj-search.ts +61 -84
  26. package/package.json +48 -47
  27. package/src/fastbrowser_cli/fastbrowser_cli.ts +50 -90
  28. package/src/fastbrowser_cli/libs/query-builder.ts +89 -0
  29. package/src/fastbrowser_cli/libs/server-manager.ts +45 -17
  30. package/src/fastbrowser_httpd/fastbrowser_httpd.ts +14 -6
  31. package/src/fastbrowser_httpd/libs/routes.ts +3 -2
  32. package/tests/http-client.test.ts +63 -0
  33. package/tests/query-builder.test.ts +204 -0
  34. package/tests/server-manager.test.ts +124 -0
  35. package/.playwright-mcp/.gitignore +0 -3
@@ -1,180 +1,159 @@
1
1
  #!/usr/bin/env npx tsx
2
2
  // Usage: npx tsx extract-wttj-job.ts <job-url>
3
3
 
4
- import { execSync } from 'node:child_process';
4
+ import { FastBrowserHelper } from "./fastbrowser_helper.js";
5
5
 
6
6
  ///////////////////////////////////////////////////////////////////////////////
7
7
 
8
8
  const ALL_STOPS = [
9
- 'Descriptif du poste',
10
- 'Missions clés',
11
- 'What you will do',
12
- 'Vos missions',
13
- 'Vos principales missions',
14
- 'Responsabilités',
15
- 'Profil recherché',
16
- 'About you',
17
- 'Votre profil',
18
- 'Le profil idéal',
19
- 'Qui êtes-vous ?',
20
- 'Location & Remote',
21
- 'What we offer',
9
+ 'Descriptif du poste',
10
+ 'Missions clés',
11
+ 'What you will do',
12
+ 'Vos missions',
13
+ 'Vos principales missions',
14
+ 'Responsabilités',
15
+ 'Profil recherché',
16
+ 'About you',
17
+ 'Votre profil',
18
+ 'Le profil idéal',
19
+ 'Qui êtes-vous ?',
20
+ 'Location & Remote',
21
+ 'What we offer',
22
22
  ];
23
23
 
24
24
  ///////////////////////////////////////////////////////////////////////////////
25
25
 
26
26
  class SnapshotParser {
27
- private lines: string[];
28
-
29
- constructor(snapshot: string) {
30
- this.lines = snapshot.split('\n');
31
- }
32
-
33
- private extractQuotedValue(line: string, token: string): string | null {
34
- const idx = line.indexOf(`${token} "`);
35
- if (idx === -1) return null;
36
- const start = idx + token.length + 2;
37
- const end = line.indexOf('"', start);
38
- if (end === -1) return null;
39
- return line.slice(start, end);
40
- }
41
-
42
- extractStaticTexts(): string[] {
43
- return this.lines
44
- .map(l => this.extractQuotedValue(l, 'StaticText'))
45
- .filter((v): v is string => v !== null && v.trim() !== '');
46
- }
47
-
48
- extractAfterHeading(headingText: string): string[] {
49
- const results: string[] = [];
50
- let found = false;
51
- for (const line of this.lines) {
52
- if (line.includes('heading "')) {
53
- const val = this.extractQuotedValue(line, 'heading');
54
- if (found) break;
55
- if (val === headingText) found = true;
56
- continue;
57
- }
58
- if (!found) continue;
59
- if (line.includes('StaticText "')) {
60
- const val = this.extractQuotedValue(line, 'StaticText');
61
- if (val === null || val.trim() === '') continue;
62
- if (ALL_STOPS.includes(val)) break;
63
- results.push(val);
64
- }
65
- }
66
- return results;
67
- }
68
-
69
- extractAfterStaticText(startLabel: string): string[] {
70
- const results: string[] = [];
71
- let found = false;
72
- for (const line of this.lines) {
73
- if (line.includes('heading "') && found) break;
74
- if (!line.includes('StaticText "')) continue;
75
- const val = this.extractQuotedValue(line, 'StaticText');
76
- if (val === null || val.trim() === '') continue;
77
- if (found) {
78
- if (ALL_STOPS.includes(val)) break;
79
- results.push(val);
80
- }
81
- if (val === startLabel) found = true;
82
- }
83
- return results;
84
- }
85
-
86
- findFirstSection(labels: string[]): string[] {
87
- for (const label of labels) {
88
- const result = this.extractAfterStaticText(label);
89
- if (result.length > 0) return result;
90
- }
91
- return [];
92
- }
93
- }
94
-
95
- ///////////////////////////////////////////////////////////////////////////////
96
-
97
- class FastBrowser {
98
- static run(command: string): string {
99
- return execSync(`npx fastbrowser_cli ${command}`, { encoding: 'utf8' });
100
- }
101
-
102
- static navigatePage(url: string): void {
103
- FastBrowser.run(`navigate_page --url '${url}'`);
104
- }
105
-
106
- static takeSnapshot(): string {
107
- return FastBrowser.run('take_snapshot');
108
- }
109
-
110
- static querySelectors(selector: string, withAncestors = true): string {
111
- const flag = withAncestors === false ? ' --no-with-ancestors' : '';
112
- return FastBrowser.run(`query_selectors --selector '${selector}'${flag}`);
113
- }
27
+ private lines: string[];
28
+
29
+ constructor(snapshot: string) {
30
+ this.lines = snapshot.split('\n');
31
+ }
32
+
33
+ private extractQuotedValue(line: string, token: string): string | null {
34
+ const idx = line.indexOf(`${token} "`);
35
+ if (idx === -1) return null;
36
+ const start = idx + token.length + 2;
37
+ const end = line.indexOf('"', start);
38
+ if (end === -1) return null;
39
+ return line.slice(start, end);
40
+ }
41
+
42
+ extractStaticTexts(): string[] {
43
+ return this.lines
44
+ .map(l => this.extractQuotedValue(l, 'StaticText'))
45
+ .filter((v): v is string => v !== null && v.trim() !== '');
46
+ }
47
+
48
+ extractAfterHeading(headingText: string): string[] {
49
+ const results: string[] = [];
50
+ let found = false;
51
+ for (const line of this.lines) {
52
+ if (line.includes('heading "')) {
53
+ const val = this.extractQuotedValue(line, 'heading');
54
+ if (found) break;
55
+ if (val === headingText) found = true;
56
+ continue;
57
+ }
58
+ if (!found) continue;
59
+ if (line.includes('StaticText "')) {
60
+ const val = this.extractQuotedValue(line, 'StaticText');
61
+ if (val === null || val.trim() === '') continue;
62
+ if (ALL_STOPS.includes(val)) break;
63
+ results.push(val);
64
+ }
65
+ }
66
+ return results;
67
+ }
68
+
69
+ extractAfterStaticText(startLabel: string): string[] {
70
+ const results: string[] = [];
71
+ let found = false;
72
+ for (const line of this.lines) {
73
+ if (line.includes('heading "') && found) break;
74
+ if (!line.includes('StaticText "')) continue;
75
+ const val = this.extractQuotedValue(line, 'StaticText');
76
+ if (val === null || val.trim() === '') continue;
77
+ if (found) {
78
+ if (ALL_STOPS.includes(val)) break;
79
+ results.push(val);
80
+ }
81
+ if (val === startLabel) found = true;
82
+ }
83
+ return results;
84
+ }
85
+
86
+ findFirstSection(labels: string[]): string[] {
87
+ for (const label of labels) {
88
+ const result = this.extractAfterStaticText(label);
89
+ if (result.length > 0) return result;
90
+ }
91
+ return [];
92
+ }
114
93
  }
115
94
 
116
95
  ///////////////////////////////////////////////////////////////////////////////
117
96
 
118
97
  class WttjExtractor {
119
- static extractTitle(output: string): string {
120
- const match = output.match(/heading "(.+?)" level/);
121
- return match !== null ? match[1] : '(non trouvé)';
122
- }
123
-
124
- static printSection(title: string, lines: string[]): void {
125
- console.log(`\n=== ${title} ===`);
126
- if (lines.length === 0) {
127
- console.log('(non trouvé)');
128
- return;
129
- }
130
- console.log(lines.join('\n'));
131
- }
132
-
133
- static async run(jobUrl: string): Promise<void> {
134
- FastBrowser.navigatePage(jobUrl);
135
-
136
- console.log('=== TITRE ===');
137
- const selectorOutput = FastBrowser.querySelectors('heading[level="2"]', false);
138
- console.log(WttjExtractor.extractTitle(selectorOutput));
139
-
140
- const snapshot = FastBrowser.takeSnapshot();
141
- const parser = new SnapshotParser(snapshot);
142
-
143
- let descriptif = parser.extractAfterHeading('Descriptif du poste');
144
- if (descriptif.length === 0) {
145
- descriptif = parser.extractAfterStaticText('Descriptif du poste');
146
- }
147
- WttjExtractor.printSection('DESCRIPTIF DU POSTE', descriptif);
148
-
149
- const missions = parser.findFirstSection([
150
- 'Missions clés',
151
- 'What you will do',
152
- 'Vos missions',
153
- 'Vos principales missions',
154
- 'Responsabilités',
155
- ]);
156
- WttjExtractor.printSection('MISSIONS CLÉS', missions);
157
-
158
- const profil = parser.findFirstSection([
159
- 'Profil recherché',
160
- 'About you',
161
- 'Votre profil',
162
- 'Le profil idéal',
163
- 'Qui êtes-vous ?',
164
- ]);
165
- WttjExtractor.printSection('PROFIL RECHERCHÉ', profil);
166
- }
98
+ static extractTitle(output: string): string {
99
+ const match = output.match(/heading "(.+?)" level/);
100
+ return match !== null ? match[1] : '(non trouvé)';
101
+ }
102
+
103
+ static printSection(title: string, lines: string[]): void {
104
+ console.log(`\n=== ${title} ===`);
105
+ if (lines.length === 0) {
106
+ console.log('(non trouvé)');
107
+ return;
108
+ }
109
+ console.log(lines.join('\n'));
110
+ }
111
+
112
+ static async run(jobUrl: string): Promise<void> {
113
+ FastBrowserHelper.navigatePage(jobUrl);
114
+
115
+ console.log('=== TITRE ===');
116
+ const selectorOutput = FastBrowserHelper.querySelectors('heading[level="2"]', false);
117
+ console.log(WttjExtractor.extractTitle(selectorOutput));
118
+
119
+ const snapshot = FastBrowserHelper.takeSnapshot();
120
+ const parser = new SnapshotParser(snapshot);
121
+
122
+ let descriptif = parser.extractAfterHeading('Descriptif du poste');
123
+ if (descriptif.length === 0) {
124
+ descriptif = parser.extractAfterStaticText('Descriptif du poste');
125
+ }
126
+ WttjExtractor.printSection('DESCRIPTIF DU POSTE', descriptif);
127
+
128
+ const missions = parser.findFirstSection([
129
+ 'Missions clés',
130
+ 'What you will do',
131
+ 'Vos missions',
132
+ 'Vos principales missions',
133
+ 'Responsabilités',
134
+ ]);
135
+ WttjExtractor.printSection('MISSIONS CLÉS', missions);
136
+
137
+ const profil = parser.findFirstSection([
138
+ 'Profil recherché',
139
+ 'About you',
140
+ 'Votre profil',
141
+ 'Le profil idéal',
142
+ 'Qui êtes-vous ?',
143
+ ]);
144
+ WttjExtractor.printSection('PROFIL RECHERCHÉ', profil);
145
+ }
167
146
  }
168
147
 
169
148
  ///////////////////////////////////////////////////////////////////////////////
170
149
 
171
150
  const jobUrl = process.argv[2];
172
151
  if (jobUrl === undefined || jobUrl === '') {
173
- console.error('Usage: npx tsx extract-wttj-job.ts <job-url>');
174
- process.exit(1);
152
+ console.error('Usage: npx tsx extract-wttj-job.ts <job-url>');
153
+ process.exit(1);
175
154
  }
176
155
 
177
156
  WttjExtractor.run(jobUrl).catch((err: unknown) => {
178
- console.error('Error:', err);
179
- process.exit(1);
157
+ console.error('Error:', err);
158
+ process.exit(1);
180
159
  });
@@ -1,101 +1,78 @@
1
1
  #!/usr/bin/env npx tsx
2
2
 
3
- import { execSync } from 'node:child_process';
3
+ // npm imports
4
4
  import { Command, Option } from 'commander';
5
+ import { FastBrowserHelper } from './fastbrowser_helper.js';
5
6
 
6
7
  ///////////////////////////////////////////////////////////////////////////////
7
-
8
- class FastBrowser {
9
- static run(command: string): string {
10
- return execSync(`npx fastbrowser_cli ${command}`, { encoding: 'utf8' });
11
- }
12
-
13
- static navigatePage(url: string): void {
14
- FastBrowser.run(`navigate_page --url '${url}'`);
15
- }
16
-
17
- static fillForm(selector: string, value: string): void {
18
- FastBrowser.run(`fill_form --selector '${selector}' --value '${value}'`);
19
- }
20
-
21
- static pressKeys(keys: string): void {
22
- FastBrowser.run(`press_keys --keys '${keys}'`);
23
- }
24
-
25
- static click(selector: string): void {
26
- FastBrowser.run(`click -s '${selector}'`);
27
- }
28
-
29
- static querySelectorsAll(selector: string, limit: number): string {
30
- return FastBrowser.run(`query_selectors_all --selector '${selector}' --limit ${limit}`);
31
- }
32
- }
33
-
8
+ ///////////////////////////////////////////////////////////////////////////////
9
+ //
10
+ ///////////////////////////////////////////////////////////////////////////////
34
11
  ///////////////////////////////////////////////////////////////////////////////
35
12
 
36
13
  class WttjSearch {
37
- static parseJobTitles(output: string): Array<{ title: string; url: string; }> {
38
- const results: Array<{ title: string; url: string; }> = [];
39
- const headingRe = /heading "(.+?)" level/g;
40
- const urlRe = /url="([^"]+)"/;
41
- for (const line of output.split('\n')) {
42
- const headingMatch = headingRe.exec(line);
43
- if (headingMatch === null) continue;
44
- const urlMatch = urlRe.exec(line);
45
- results.push({
46
- title: headingMatch[1],
47
- url: urlMatch !== null ? urlMatch[1] : '',
48
- });
49
- headingRe.lastIndex = 0;
50
- }
51
- return results;
52
- }
53
-
54
- static outputJson(jobs: Array<{ title: string; url: string; }>): void {
55
- console.log(JSON.stringify(jobs, null, 2));
56
- }
57
-
58
- static outputMarkdown(jobs: Array<{ title: string; url: string; }>): void {
59
- console.log('| # | Title | URL |');
60
- console.log('|---|-------|-----|');
61
- for (const [i, job] of jobs.entries()) {
62
- const url = job.url !== '' ? `[link](${job.url})` : '';
63
- console.log(`| ${i + 1} | ${job.title} | ${url} |`);
64
- }
65
- }
66
-
67
- static run(query: string, limit: number = 1, offset: number = 1, format: 'json' | 'markdown' = 'markdown'): void {
68
- FastBrowser.navigatePage('https://www.welcometothejungle.com/fr');
69
- FastBrowser.fillForm('combobox[name*="intitulé de poste"]', query);
70
- FastBrowser.pressKeys('Enter');
71
-
72
- let allJobs: Array<{ title: string; url: string; }> = [];
73
-
74
- for (let page = offset; page < offset + limit; page++) {
75
- if (page > 1) {
76
- FastBrowser.click(`link[name="${page}"]`);
77
- }
78
- const output = FastBrowser.querySelectorsAll('heading[level="2"]', 30);
79
- allJobs = allJobs.concat(WttjSearch.parseJobTitles(output));
80
- }
81
-
82
- if (format === 'json') {
83
- WttjSearch.outputJson(allJobs);
84
- } else {
85
- WttjSearch.outputMarkdown(allJobs);
86
- }
87
- }
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
+ }
88
65
  }
89
66
 
90
67
  ///////////////////////////////////////////////////////////////////////////////
91
68
 
92
69
  const program = new Command();
93
70
  program
94
- .argument('[query]', 'job search query', 'machine learning')
95
- .option('-l, --limit <number>', 'number of pages to fetch', '1')
96
- .option('-o, --offset <number>', 'first page to scrape', '1')
97
- .addOption(new Option('-f, --format <format>', 'output format: json or markdown').choices(['json', 'markdown']).default('markdown'))
98
- .parse();
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();
99
76
 
100
77
  const opts = program.opts();
101
78
  const query = program.args[0] ?? 'machine learning';
package/package.json CHANGED
@@ -1,48 +1,49 @@
1
1
  {
2
- "name": "fastbrowser_cli",
3
- "version": "1.0.22",
4
- "description": "",
5
- "main": "dist/fastbrowser_cli/fastbrowser_cli.js",
6
- "bin": {
7
- "fastbrowser_cli": "./dist/fastbrowser_cli/fastbrowser_cli.js",
8
- "fastbrowser_mcp": "./dist/fastbrowser_mcp/fastbrowser_mcp.js"
9
- },
10
- "scripts": {
11
- "dev:fastbrowser_mcp": "npx tsx ./src/fastbrowser_mcp/fastbrowser_mcp.ts",
12
- "dev:httpd": "npx tsx ./src/fastbrowser_httpd/fastbrowser_httpd.ts",
13
- "dev:cli": "npx tsx ./src/fastbrowser_cli/fastbrowser_cli.ts",
14
- "inspect:fastbrowser_mcp:chrome_devtools": "npx @modelcontextprotocol/inspector npx tsx ./src/fastbrowser_mcp/fastbrowser_mcp.ts mcp_server --mcp_target chrome_devtools",
15
- "inspect:fastbrowser_mcp:playwright": "npx @modelcontextprotocol/inspector npx tsx ./src/fastbrowser_mcp/fastbrowser_mcp.ts mcp_server -v --mcp_target playwright",
16
- "inspect:chrome_devtools": "npx @modelcontextprotocol/inspector npx chrome-devtools-mcp@latest --autoconnect",
17
- "inspect:playwright:chrome": "npx @modelcontextprotocol/inspector npx @playwright/mcp@latest --cdp-endpoint=chrome",
18
- "inspect:playwright:cdp_endpoint": "npx @modelcontextprotocol/inspector npx @playwright/mcp@latest --cdp-endpoint=http://localhost:9222",
19
- "inspect:playwright:extension": "npx @modelcontextprotocol/inspector npx @playwright/mcp@latest --extension",
20
- "build": "tsc -p tsconfig.build.json",
21
- "publish:all": "npm run build && npm version patch && npm publish --access public",
22
- "typecheck": "tsc -p tsconfig.json --noEmit"
23
- },
24
- "keywords": [],
25
- "author": "",
26
- "license": "MIT",
27
- "repository": {
28
- "type": "git",
29
- "url": "https://github.com/jeromeetienne/skillmd_collecetion.git"
30
- },
31
- "homepage": "https://github.com/jeromeetienne/skillmd_collection/packages/fastbrowser_cli#readme",
32
- "type": "module",
33
- "dependencies": {
34
- "@modelcontextprotocol/sdk": "^1.29.0",
35
- "a11y_parse": "^1.0.2",
36
- "commander": "^12.1.0",
37
- "express": "^4.21.2",
38
- "string-argv": "^0.3.2",
39
- "typescript": "^6.0.3",
40
- "zod": "^4.3.6",
41
- "zod-from-json-schema": "^0.5.2"
42
- },
43
- "devDependencies": {
44
- "@types/express": "^4.17.21",
45
- "@types/node": "^25.6.0",
46
- "tsx": "^4.19.2"
47
- }
48
- }
2
+ "name": "fastbrowser_cli",
3
+ "version": "1.0.28",
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
+ "main": "dist/fastbrowser_cli/fastbrowser_cli.js",
6
+ "bin": {
7
+ "fastbrowser_cli": "./dist/fastbrowser_cli/fastbrowser_cli.js",
8
+ "fastbrowser_mcp": "./dist/fastbrowser_mcp/fastbrowser_mcp.js"
9
+ },
10
+ "keywords": [],
11
+ "author": "",
12
+ "license": "MIT",
13
+ "repository": {
14
+ "type": "git",
15
+ "url": "https://github.com/jeromeetienne/skillmd_collecetion.git"
16
+ },
17
+ "homepage": "https://github.com/jeromeetienne/skillmd_collection/packages/fastbrowser_cli#readme",
18
+ "type": "module",
19
+ "dependencies": {
20
+ "@modelcontextprotocol/sdk": "^1.29.0",
21
+ "commander": "^12.1.0",
22
+ "express": "^4.21.2",
23
+ "string-argv": "^0.3.2",
24
+ "typescript": "^6.0.3",
25
+ "zod": "^4.3.6",
26
+ "zod-from-json-schema": "^0.5.2",
27
+ "a11y_parse": "^1.0.5"
28
+ },
29
+ "devDependencies": {
30
+ "@types/express": "^4.17.21",
31
+ "@types/node": "^25.6.0",
32
+ "tsx": "^4.19.2"
33
+ },
34
+ "scripts": {
35
+ "dev:mcp": "npx tsx ./src/fastbrowser_mcp/fastbrowser_mcp.ts",
36
+ "dev:httpd": "npx tsx ./src/fastbrowser_httpd/fastbrowser_httpd.ts",
37
+ "dev:cli": "npx tsx ./src/fastbrowser_cli/fastbrowser_cli.ts",
38
+ "inspect:fastbrowser_mcp:chrome_devtools": "npx @modelcontextprotocol/inspector npx tsx ./src/fastbrowser_mcp/fastbrowser_mcp.ts mcp_server --mcp_target chrome_devtools",
39
+ "inspect:fastbrowser_mcp:playwright": "npx @modelcontextprotocol/inspector npx tsx ./src/fastbrowser_mcp/fastbrowser_mcp.ts mcp_server -v --mcp_target playwright",
40
+ "inspect:chrome_devtools": "npx @modelcontextprotocol/inspector npx chrome-devtools-mcp@latest --autoconnect",
41
+ "inspect:playwright:chrome": "npx @modelcontextprotocol/inspector npx @playwright/mcp@latest --cdp-endpoint=chrome",
42
+ "inspect:playwright:cdp_endpoint": "npx @modelcontextprotocol/inspector npx @playwright/mcp@latest --cdp-endpoint=http://localhost:9222",
43
+ "inspect:playwright:extension": "npx @modelcontextprotocol/inspector npx @playwright/mcp@latest --extension",
44
+ "build": "tsc -p tsconfig.build.json",
45
+ "publish:all": "pnpm run build && npm version patch && pnpm publish --access public",
46
+ "typecheck": "tsc -p tsconfig.json --noEmit",
47
+ "test": "npx tsx --test 'tests/**/*.test.ts'"
48
+ }
49
+ }