fastbrowser_cli 1.0.14 → 1.0.16

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 (154) hide show
  1. package/.playwright-mcp/.gitignore +3 -0
  2. package/README.md +29 -3
  3. package/dist/fastbrowser_cli/fastbrowser_cli.js +52 -35
  4. package/dist/fastbrowser_cli/fastbrowser_cli.js.map +1 -1
  5. package/dist/fastbrowser_cli/libs/http-client.js +3 -7
  6. package/dist/fastbrowser_cli/libs/http-client.js.map +1 -1
  7. package/dist/fastbrowser_cli/libs/server-manager.d.ts.map +1 -1
  8. package/dist/fastbrowser_cli/libs/server-manager.js +30 -28
  9. package/dist/fastbrowser_cli/libs/server-manager.js.map +1 -1
  10. package/dist/fastbrowser_httpd/fastbrowser_httpd.js +22 -18
  11. package/dist/fastbrowser_httpd/fastbrowser_httpd.js.map +1 -1
  12. package/dist/fastbrowser_httpd/libs/routes.d.ts +2 -2
  13. package/dist/fastbrowser_httpd/libs/routes.d.ts.map +1 -1
  14. package/dist/fastbrowser_httpd/libs/routes.js +4 -8
  15. package/dist/fastbrowser_httpd/libs/routes.js.map +1 -1
  16. package/dist/fastbrowser_httpd/libs/tool-schemas.js +39 -42
  17. package/dist/fastbrowser_httpd/libs/tool-schemas.js.map +1 -1
  18. package/dist/fastbrowser_mcp/fastbrowser_mcp.d.ts.map +1 -1
  19. package/dist/fastbrowser_mcp/fastbrowser_mcp.js +270 -187
  20. package/dist/fastbrowser_mcp/fastbrowser_mcp.js.map +1 -1
  21. package/dist/fastbrowser_mcp/fastbrowser_types.d.ts +5 -0
  22. package/dist/fastbrowser_mcp/fastbrowser_types.d.ts.map +1 -0
  23. package/dist/fastbrowser_mcp/fastbrowser_types.js +2 -0
  24. package/dist/fastbrowser_mcp/fastbrowser_types.js.map +1 -0
  25. package/dist/{fastweb_mcp/libs/mcp_client.d.ts → fastbrowser_mcp/libs/mcp_client_TOREMOVE.d.ts} +6 -2
  26. package/dist/fastbrowser_mcp/libs/mcp_client_TOREMOVE.d.ts.map +1 -0
  27. package/dist/{src/libs/mcp_client.js → fastbrowser_mcp/libs/mcp_client_TOREMOVE.js} +15 -12
  28. package/dist/fastbrowser_mcp/libs/mcp_client_TOREMOVE.js.map +1 -0
  29. package/dist/{src/libs/mcp_client.d.ts → fastbrowser_mcp/libs/mcp_my_client.d.ts} +6 -2
  30. package/dist/fastbrowser_mcp/libs/mcp_my_client.d.ts.map +1 -0
  31. package/dist/{fastweb_mcp/libs/mcp_client.js → fastbrowser_mcp/libs/mcp_my_client.js} +15 -12
  32. package/dist/fastbrowser_mcp/libs/mcp_my_client.js.map +1 -0
  33. package/dist/fastbrowser_mcp/libs/mcp_proxy.d.ts +2 -2
  34. package/dist/fastbrowser_mcp/libs/mcp_proxy.d.ts.map +1 -1
  35. package/dist/fastbrowser_mcp/libs/mcp_proxy.js +9 -16
  36. package/dist/fastbrowser_mcp/libs/mcp_proxy.js.map +1 -1
  37. package/dist/fastbrowser_mcp/libs/mcp_target_helper.d.ts +35 -0
  38. package/dist/fastbrowser_mcp/libs/mcp_target_helper.d.ts.map +1 -0
  39. package/dist/fastbrowser_mcp/libs/mcp_target_helper.js +161 -0
  40. package/dist/fastbrowser_mcp/libs/mcp_target_helper.js.map +1 -0
  41. package/dist/fastbrowser_mcp/libs/playwright_a11y_helper.d.ts +28 -0
  42. package/dist/fastbrowser_mcp/libs/playwright_a11y_helper.d.ts.map +1 -0
  43. package/dist/fastbrowser_mcp/libs/playwright_a11y_helper.js +210 -0
  44. package/dist/fastbrowser_mcp/libs/playwright_a11y_helper.js.map +1 -0
  45. package/dist/fastbrowser_mcp/libs/response_formatter.d.ts +10 -0
  46. package/dist/fastbrowser_mcp/libs/response_formatter.d.ts.map +1 -0
  47. package/dist/fastbrowser_mcp/libs/response_formatter.js +155 -0
  48. package/dist/fastbrowser_mcp/libs/response_formatter.js.map +1 -0
  49. package/dist/fastbrowser_mcp/libs/schemas.js +14 -17
  50. package/dist/fastbrowser_mcp/libs/schemas.js.map +1 -1
  51. package/examples/mcp_client_playwright.ts +34 -0
  52. package/examples/welcometothejungle/wttj-job.ts +180 -0
  53. package/examples/welcometothejungle/wttj-search.ts +105 -0
  54. package/outputs/.gitignore +3 -0
  55. package/package.json +12 -8
  56. package/src/fastbrowser_cli/fastbrowser_cli.ts +34 -11
  57. package/src/fastbrowser_cli/libs/server-manager.ts +12 -3
  58. package/src/fastbrowser_httpd/fastbrowser_httpd.ts +16 -5
  59. package/src/fastbrowser_httpd/libs/routes.ts +2 -2
  60. package/src/fastbrowser_mcp/fastbrowser_mcp.ts +324 -150
  61. package/src/fastbrowser_mcp/fastbrowser_types.ts +4 -0
  62. package/src/fastbrowser_mcp/libs/{mcp_client.ts → mcp_client_TOREMOVE.ts} +13 -1
  63. package/src/fastbrowser_mcp/libs/mcp_my_client.ts +128 -0
  64. package/src/fastbrowser_mcp/libs/mcp_proxy.ts +2 -2
  65. package/src/fastbrowser_mcp/libs/mcp_target_helper.ts +164 -0
  66. package/src/fastbrowser_mcp/libs/playwright_a11y_helper.ts +249 -0
  67. package/src/fastbrowser_mcp/libs/response_formatter.ts +162 -0
  68. package/src/fastbrowser_mcp/libs/schemas.ts +2 -2
  69. package/tsconfig.build.json +13 -0
  70. package/tsconfig.json +10 -22
  71. package/dist/contrib/fastweb-cli/fastweb-cli.d.ts +0 -3
  72. package/dist/contrib/fastweb-cli/fastweb-cli.d.ts.map +0 -1
  73. package/dist/contrib/fastweb-cli/fastweb-cli.js +0 -151
  74. package/dist/contrib/fastweb-cli/fastweb-cli.js.map +0 -1
  75. package/dist/contrib/fastweb-cli/http-client.d.ts +0 -7
  76. package/dist/contrib/fastweb-cli/http-client.d.ts.map +0 -1
  77. package/dist/contrib/fastweb-cli/http-client.js +0 -51
  78. package/dist/contrib/fastweb-cli/http-client.js.map +0 -1
  79. package/dist/contrib/fastweb-http-server/fastweb-http-server.d.ts +0 -3
  80. package/dist/contrib/fastweb-http-server/fastweb-http-server.d.ts.map +0 -1
  81. package/dist/contrib/fastweb-http-server/fastweb-http-server.js +0 -82
  82. package/dist/contrib/fastweb-http-server/fastweb-http-server.js.map +0 -1
  83. package/dist/contrib/fastweb-http-server/routes.d.ts +0 -6
  84. package/dist/contrib/fastweb-http-server/routes.d.ts.map +0 -1
  85. package/dist/contrib/fastweb-http-server/routes.js +0 -41
  86. package/dist/contrib/fastweb-http-server/routes.js.map +0 -1
  87. package/dist/contrib/fastweb-http-server/tool-schemas.d.ts +0 -63
  88. package/dist/contrib/fastweb-http-server/tool-schemas.d.ts.map +0 -1
  89. package/dist/contrib/fastweb-http-server/tool-schemas.js +0 -61
  90. package/dist/contrib/fastweb-http-server/tool-schemas.js.map +0 -1
  91. package/dist/fastbrowser_mcp/libs/mcp_client.d.ts +0 -120
  92. package/dist/fastbrowser_mcp/libs/mcp_client.d.ts.map +0 -1
  93. package/dist/fastbrowser_mcp/libs/mcp_client.js +0 -83
  94. package/dist/fastbrowser_mcp/libs/mcp_client.js.map +0 -1
  95. package/dist/fastweb_cli/fastweb_cli.d.ts +0 -3
  96. package/dist/fastweb_cli/fastweb_cli.d.ts.map +0 -1
  97. package/dist/fastweb_cli/fastweb_cli.js +0 -254
  98. package/dist/fastweb_cli/fastweb_cli.js.map +0 -1
  99. package/dist/fastweb_cli/http-client.d.ts +0 -7
  100. package/dist/fastweb_cli/http-client.d.ts.map +0 -1
  101. package/dist/fastweb_cli/http-client.js +0 -51
  102. package/dist/fastweb_cli/http-client.js.map +0 -1
  103. package/dist/fastweb_cli/libs/http-client.d.ts +0 -7
  104. package/dist/fastweb_cli/libs/http-client.d.ts.map +0 -1
  105. package/dist/fastweb_cli/libs/http-client.js +0 -51
  106. package/dist/fastweb_cli/libs/http-client.js.map +0 -1
  107. package/dist/fastweb_cli/libs/server-manager.d.ts +0 -12
  108. package/dist/fastweb_cli/libs/server-manager.d.ts.map +0 -1
  109. package/dist/fastweb_cli/libs/server-manager.js +0 -194
  110. package/dist/fastweb_cli/libs/server-manager.js.map +0 -1
  111. package/dist/fastweb_http_server/fastweb_http_server.d.ts +0 -3
  112. package/dist/fastweb_http_server/fastweb_http_server.d.ts.map +0 -1
  113. package/dist/fastweb_http_server/fastweb_http_server.js +0 -82
  114. package/dist/fastweb_http_server/fastweb_http_server.js.map +0 -1
  115. package/dist/fastweb_http_server/libs/routes.d.ts +0 -6
  116. package/dist/fastweb_http_server/libs/routes.d.ts.map +0 -1
  117. package/dist/fastweb_http_server/libs/routes.js +0 -41
  118. package/dist/fastweb_http_server/libs/routes.js.map +0 -1
  119. package/dist/fastweb_http_server/libs/tool-schemas.d.ts +0 -72
  120. package/dist/fastweb_http_server/libs/tool-schemas.d.ts.map +0 -1
  121. package/dist/fastweb_http_server/libs/tool-schemas.js +0 -65
  122. package/dist/fastweb_http_server/libs/tool-schemas.js.map +0 -1
  123. package/dist/fastweb_http_server/routes.d.ts +0 -6
  124. package/dist/fastweb_http_server/routes.d.ts.map +0 -1
  125. package/dist/fastweb_http_server/routes.js +0 -41
  126. package/dist/fastweb_http_server/routes.js.map +0 -1
  127. package/dist/fastweb_http_server/tool-schemas.d.ts +0 -63
  128. package/dist/fastweb_http_server/tool-schemas.d.ts.map +0 -1
  129. package/dist/fastweb_http_server/tool-schemas.js +0 -61
  130. package/dist/fastweb_http_server/tool-schemas.js.map +0 -1
  131. package/dist/fastweb_mcp/fastweb_mcp.d.ts +0 -4
  132. package/dist/fastweb_mcp/fastweb_mcp.d.ts.map +0 -1
  133. package/dist/fastweb_mcp/fastweb_mcp.js +0 -417
  134. package/dist/fastweb_mcp/fastweb_mcp.js.map +0 -1
  135. package/dist/fastweb_mcp/libs/mcp_client.d.ts.map +0 -1
  136. package/dist/fastweb_mcp/libs/mcp_client.js.map +0 -1
  137. package/dist/fastweb_mcp/libs/mcp_proxy.d.ts +0 -10
  138. package/dist/fastweb_mcp/libs/mcp_proxy.d.ts.map +0 -1
  139. package/dist/fastweb_mcp/libs/mcp_proxy.js +0 -45
  140. package/dist/fastweb_mcp/libs/mcp_proxy.js.map +0 -1
  141. package/dist/fastweb_mcp/libs/schemas.d.ts +0 -28
  142. package/dist/fastweb_mcp/libs/schemas.d.ts.map +0 -1
  143. package/dist/fastweb_mcp/libs/schemas.js +0 -38
  144. package/dist/fastweb_mcp/libs/schemas.js.map +0 -1
  145. package/dist/src/fastweb_mcp.d.ts +0 -17
  146. package/dist/src/fastweb_mcp.d.ts.map +0 -1
  147. package/dist/src/fastweb_mcp.js +0 -342
  148. package/dist/src/fastweb_mcp.js.map +0 -1
  149. package/dist/src/libs/mcp_client.d.ts.map +0 -1
  150. package/dist/src/libs/mcp_client.js.map +0 -1
  151. package/dist/src/libs/mcp_proxy.d.ts +0 -10
  152. package/dist/src/libs/mcp_proxy.d.ts.map +0 -1
  153. package/dist/src/libs/mcp_proxy.js +0 -45
  154. package/dist/src/libs/mcp_proxy.js.map +0 -1
@@ -0,0 +1,180 @@
1
+ #!/usr/bin/env npx tsx
2
+ // Usage: npx tsx extract-wttj-job.ts <job-url>
3
+
4
+ import { execSync } from 'node:child_process';
5
+
6
+ ///////////////////////////////////////////////////////////////////////////////
7
+
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',
22
+ ];
23
+
24
+ ///////////////////////////////////////////////////////////////////////////////
25
+
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
+ }
114
+ }
115
+
116
+ ///////////////////////////////////////////////////////////////////////////////
117
+
118
+ 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
+ }
167
+ }
168
+
169
+ ///////////////////////////////////////////////////////////////////////////////
170
+
171
+ const jobUrl = process.argv[2];
172
+ if (jobUrl === undefined || jobUrl === '') {
173
+ console.error('Usage: npx tsx extract-wttj-job.ts <job-url>');
174
+ process.exit(1);
175
+ }
176
+
177
+ WttjExtractor.run(jobUrl).catch((err: unknown) => {
178
+ console.error('Error:', err);
179
+ process.exit(1);
180
+ });
@@ -0,0 +1,105 @@
1
+ #!/usr/bin/env npx tsx
2
+
3
+ import { execSync } from 'node:child_process';
4
+ import { Command, Option } from 'commander';
5
+
6
+ ///////////////////////////////////////////////////////////////////////////////
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
+
34
+ ///////////////////////////////////////////////////////////////////////////////
35
+
36
+ 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
+ }
88
+ }
89
+
90
+ ///////////////////////////////////////////////////////////////////////////////
91
+
92
+ const program = new Command();
93
+ 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();
99
+
100
+ const opts = program.opts();
101
+ const query = program.args[0] ?? 'machine learning';
102
+ const limit = Number(opts['limit']);
103
+ const offset = Number(opts['offset']);
104
+ const format: 'json' | 'markdown' = opts['format'] === 'json' ? 'json' : 'markdown';
105
+ WttjSearch.run(query, limit, offset, format);
@@ -0,0 +1,3 @@
1
+ # ignore all but .gitignore
2
+ *
3
+ !.gitignore
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fastbrowser_cli",
3
- "version": "1.0.14",
3
+ "version": "1.0.16",
4
4
  "description": "",
5
5
  "main": "dist/fastbrowser_cli/fastbrowser_cli.js",
6
6
  "bin": {
@@ -9,13 +9,17 @@
9
9
  },
10
10
  "scripts": {
11
11
  "start:fastbrowser_mcp": "npx tsx ./src/fastbrowser_mcp/fastbrowser_mcp.ts",
12
- "start:http-server": "npx tsx ./src/fastbrowser_httpd/fastbrowser_httpd.ts",
12
+ "start:httpd": "npx tsx ./src/fastbrowser_httpd/fastbrowser_httpd.ts",
13
13
  "start:cli": "npx tsx ./src/fastbrowser_cli/fastbrowser_cli.ts",
14
- "inspect:fastbrowser_mcp": "npx @modelcontextprotocol/inspector npx tsx ./src/fastbrowser_mcp/fastbrowser_mcp.ts mcp_server",
15
- "inspect:chrome-devtools": "npx @modelcontextprotocol/inspector npx chrome-devtools-mcp@latest --autoconnect",
16
- "build": "tsc -p tsconfig.json",
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",
17
21
  "publish:all": "npm run build && npm version patch && npm publish --access public",
18
- "typecheck": "tsc --noEmit"
22
+ "typecheck": "tsc -p tsconfig.json --noEmit"
19
23
  },
20
24
  "keywords": [],
21
25
  "author": "",
@@ -25,10 +29,10 @@
25
29
  "url": "https://github.com/jeromeetienne/skillmd_collecetion.git"
26
30
  },
27
31
  "homepage": "https://github.com/jeromeetienne/skillmd_collection/packages/fastbrowser_cli#readme",
28
- "type": "commonjs",
32
+ "type": "module",
29
33
  "dependencies": {
30
34
  "@modelcontextprotocol/sdk": "^1.29.0",
31
- "a11y_parse": "^1.0.1",
35
+ "a11y_parse": "file:../a11y_parse",
32
36
  "commander": "^12.1.0",
33
37
  "express": "^4.21.2",
34
38
  "string-argv": "^0.3.2",
@@ -26,18 +26,34 @@ class SilentExitError extends Error {
26
26
  ///////////////////////////////////////////////////////////////////////////////
27
27
 
28
28
  class MainHelper {
29
- static getServerFromCmd(cmd: Command): string {
29
+ /**
30
+ * Get the server URL from the command options, environment variable, or default
31
+ * @param cmd
32
+ * @returns
33
+ */
34
+ static getServerUrlFromCmd(cmd: Command): string {
30
35
  const globalOpts = cmd.optsWithGlobals<GlobalOpts>();
31
36
  return HttpClient.getServerUrl(globalOpts.server);
32
37
  }
33
38
 
39
+ /**
40
+ * Determine whether to auto-start the server based on command options (default: true)
41
+ * @param cmd
42
+ * @returns
43
+ */
34
44
  static getAutostartFromCmd(cmd: Command): boolean {
35
45
  const globalOpts = cmd.optsWithGlobals<GlobalOpts>();
36
46
  return globalOpts.autostart !== false;
37
47
  }
38
48
 
49
+ /**
50
+ * Run a tool by making an HTTP request to the server, with optional auto-start
51
+ * @param cmd
52
+ * @param routeName
53
+ * @param body
54
+ */
39
55
  static async runTool(cmd: Command, routeName: string, body: unknown): Promise<void> {
40
- const server = MainHelper.getServerFromCmd(cmd);
56
+ const server = MainHelper.getServerUrlFromCmd(cmd);
41
57
  if (MainHelper.getAutostartFromCmd(cmd) === true) {
42
58
  await ServerManager.ensureRunning(server);
43
59
  }
@@ -116,7 +132,7 @@ class MainHelper {
116
132
  }
117
133
 
118
134
  static async runInstall(skillFolder: string): Promise<void> {
119
- const sourceSkillMd = path.resolve(__dirname, '../../skills/fastbrowser/SKILL.md');
135
+ const sourceSkillMd = path.resolve(import.meta.dirname, '../../skills/fastbrowser/SKILL.md');
120
136
  const targetDir = path.resolve(skillFolder, 'skills', 'fastbrowser');
121
137
  const targetSkillMd = path.join(targetDir, 'SKILL.md');
122
138
  try {
@@ -226,28 +242,35 @@ async function main(): Promise<void> {
226
242
  .command('start')
227
243
  .description('Start the fastbrowser HTTP server as a detached daemon')
228
244
  .action(async (_opts, cmd: Command) => {
229
- const server = MainHelper.getServerFromCmd(cmd);
230
- await ServerManager.start(server);
245
+ const serverUrl = MainHelper.getServerUrlFromCmd(cmd);
246
+ await ServerManager.start(serverUrl);
231
247
  });
232
248
 
233
249
  serverCmd
234
250
  .command('stop')
235
251
  .description('Stop the fastbrowser HTTP server')
236
252
  .action(async (_opts, cmd: Command) => {
237
- const server = MainHelper.getServerFromCmd(cmd);
238
- await ServerManager.stop(server);
253
+ const serverUrl = MainHelper.getServerUrlFromCmd(cmd);
254
+ await ServerManager.stop(serverUrl);
239
255
  });
240
256
 
241
257
  serverCmd
242
258
  .command('status')
243
259
  .description('Report whether the fastbrowser HTTP server is running')
244
260
  .action(async (_opts, cmd: Command) => {
245
- const server = MainHelper.getServerFromCmd(cmd);
246
- const state = await ServerManager.status(server);
247
- console.log(`fastbrowser server at ${server}: ${state}`);
248
- if (state === 'stopped') throw new SilentExitError();
261
+ const serverUrl = MainHelper.getServerUrlFromCmd(cmd);
262
+ const serverStatus = await ServerManager.status(serverUrl);
263
+ console.log(`fastbrowser server at ${serverUrl}: ${serverStatus}`);
249
264
  });
250
265
 
266
+ serverCmd
267
+ .command('restart')
268
+ .description('Restart the fastbrowser HTTP server')
269
+ .action(async (_opts, cmd: Command) => {
270
+ const serverUrl = MainHelper.getServerUrlFromCmd(cmd);
271
+ await ServerManager.stop(serverUrl);
272
+ await ServerManager.start(serverUrl);
273
+ });
251
274
  ///////////////////////////////////////////////////////////////////////////////
252
275
  ///////////////////////////////////////////////////////////////////////////////
253
276
  //
@@ -63,14 +63,23 @@ export class ServerManager {
63
63
  throw new Error(`Refusing to start: ${serverUrl} is not a local URL`);
64
64
  }
65
65
 
66
+ // debugger
67
+ let entryPath = Path.resolve(import.meta.dirname, '..', '..', 'fastbrowser_httpd', 'fastbrowser_httpd.js');
66
68
  const port = ServerManager.parsePort(serverUrl);
67
- const entryPath = Path.resolve(__dirname, '..', '..', 'fastbrowser_httpd', 'fastbrowser_httpd.js');
68
- const packageRoot = Path.resolve(__dirname, '..', '..', '..');
69
+ let spawnCommand = process.execPath;
70
+ let spawnArgs = [entryPath, '--port', String(port)]
71
+ // trick to work without being in `./dist'
72
+ if (entryPath.includes('/dist/') === false) {
73
+ spawnCommand = '/usr/local/bin/npx';
74
+ spawnArgs[0] = spawnArgs[0].replace(/\.js$/, '.ts');
75
+ spawnArgs = ['tsx', ...spawnArgs];
76
+ }
77
+ const packageRoot = Path.resolve(import.meta.dirname, '..', '..', '..');
69
78
 
70
79
  Fs.mkdirSync(STATE_DIR, { recursive: true });
71
80
  const logFd = Fs.openSync(LOG_FILE, 'a');
72
81
 
73
- const child = spawn(process.execPath, [entryPath, '--port', String(port)], {
82
+ const child = spawn(spawnCommand, spawnArgs, {
74
83
  detached: true,
75
84
  stdio: ['ignore', logFd, logFd],
76
85
  cwd: packageRoot,
@@ -8,9 +8,10 @@ import { Command } from 'commander';
8
8
  import express from 'express';
9
9
 
10
10
  // local imports
11
- import { McpClient } from '../fastbrowser_mcp/libs/mcp_client.js';
11
+ import { McpMyClient } from '../fastbrowser_mcp/libs/mcp_my_client.js';
12
12
  import { Routes } from './libs/routes.js';
13
13
 
14
+
14
15
  ///////////////////////////////////////////////////////////////////////////////
15
16
  ///////////////////////////////////////////////////////////////////////////////
16
17
  //
@@ -26,14 +27,24 @@ class MainHelper {
26
27
  verbose?: boolean;
27
28
  }): Promise<void> {
28
29
  // Spawn fastbrowser-mcp as a subprocess and hold a persistent MCP client to it.
29
- const fastbrowserMcpEntry = Path.resolve(__dirname, '..', 'fastbrowser_mcp', 'fastbrowser_mcp.js');
30
- const mcpClient = new McpClient({
30
+ const fastbrowserMcpEntry = Path.resolve(import.meta.dirname, '..', 'fastbrowser_mcp', 'fastbrowser_mcp.js');
31
+ let mcpServerCommand = process.execPath;
32
+ let mcpServerArgs = [fastbrowserMcpEntry, 'mcp_server'];
33
+ // trick to work without being in `./dist'
34
+ if (fastbrowserMcpEntry.includes('/dist/') === false) {
35
+ mcpServerCommand = '/usr/local/bin/npx';
36
+ mcpServerArgs[0] = mcpServerArgs[0].replace(/\.js$/, '.ts');
37
+ mcpServerArgs = ['tsx', ...mcpServerArgs];
38
+ }
39
+
40
+ const mcpClient = new McpMyClient({
31
41
  name: 'fastbrowser-httpd',
32
42
  version: '1.0.0',
43
+ mcpTarget: 'chrome_devtools',
33
44
  transport: {
34
45
  type: 'stdio',
35
- command: process.execPath,
36
- args: [fastbrowserMcpEntry, 'mcp_server'],
46
+ command: mcpServerCommand,
47
+ args: mcpServerArgs,
37
48
  },
38
49
  });
39
50
 
@@ -2,7 +2,7 @@
2
2
  import type { Express, Request, Response } from 'express';
3
3
 
4
4
  // local imports
5
- import { McpClient } from '../../fastbrowser_mcp/libs/mcp_client.js';
5
+ import { McpMyClient } from '../../fastbrowser_mcp/libs/mcp_my_client.js';
6
6
  import { TOOL_SCHEMAS, ToolResponseSchema } from './tool-schemas.js';
7
7
 
8
8
  ///////////////////////////////////////////////////////////////////////////////
@@ -12,7 +12,7 @@ import { TOOL_SCHEMAS, ToolResponseSchema } from './tool-schemas.js';
12
12
  ///////////////////////////////////////////////////////////////////////////////
13
13
 
14
14
  export class Routes {
15
- static register(app: Express, mcpClient: McpClient): void {
15
+ static register(app: Express, mcpClient: McpMyClient): void {
16
16
  app.get('/health', (_req: Request, res: Response) => {
17
17
  res.json({ ok: true });
18
18
  });