fastbrowser_cli 1.0.13 → 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 (156) hide show
  1. package/.playwright-mcp/.gitignore +3 -0
  2. package/README.md +40 -1
  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 +14 -9
  56. package/skills/fastbrowser/SKILL.md +2 -2
  57. package/src/fastbrowser_cli/fastbrowser_cli.ts +34 -11
  58. package/src/fastbrowser_cli/libs/server-manager.ts +12 -3
  59. package/src/fastbrowser_httpd/fastbrowser_httpd.ts +16 -5
  60. package/src/fastbrowser_httpd/libs/routes.ts +2 -2
  61. package/src/fastbrowser_mcp/fastbrowser_mcp.ts +324 -150
  62. package/src/fastbrowser_mcp/fastbrowser_types.ts +4 -0
  63. package/src/fastbrowser_mcp/libs/{mcp_client.ts → mcp_client_TOREMOVE.ts} +13 -1
  64. package/src/fastbrowser_mcp/libs/mcp_my_client.ts +128 -0
  65. package/src/fastbrowser_mcp/libs/mcp_proxy.ts +2 -2
  66. package/src/fastbrowser_mcp/libs/mcp_target_helper.ts +164 -0
  67. package/src/fastbrowser_mcp/libs/playwright_a11y_helper.ts +249 -0
  68. package/src/fastbrowser_mcp/libs/response_formatter.ts +162 -0
  69. package/src/fastbrowser_mcp/libs/schemas.ts +2 -2
  70. package/tsconfig.build.json +13 -0
  71. package/tsconfig.json +10 -22
  72. package/dist/contrib/fastweb-cli/fastweb-cli.d.ts +0 -3
  73. package/dist/contrib/fastweb-cli/fastweb-cli.d.ts.map +0 -1
  74. package/dist/contrib/fastweb-cli/fastweb-cli.js +0 -151
  75. package/dist/contrib/fastweb-cli/fastweb-cli.js.map +0 -1
  76. package/dist/contrib/fastweb-cli/http-client.d.ts +0 -7
  77. package/dist/contrib/fastweb-cli/http-client.d.ts.map +0 -1
  78. package/dist/contrib/fastweb-cli/http-client.js +0 -51
  79. package/dist/contrib/fastweb-cli/http-client.js.map +0 -1
  80. package/dist/contrib/fastweb-http-server/fastweb-http-server.d.ts +0 -3
  81. package/dist/contrib/fastweb-http-server/fastweb-http-server.d.ts.map +0 -1
  82. package/dist/contrib/fastweb-http-server/fastweb-http-server.js +0 -82
  83. package/dist/contrib/fastweb-http-server/fastweb-http-server.js.map +0 -1
  84. package/dist/contrib/fastweb-http-server/routes.d.ts +0 -6
  85. package/dist/contrib/fastweb-http-server/routes.d.ts.map +0 -1
  86. package/dist/contrib/fastweb-http-server/routes.js +0 -41
  87. package/dist/contrib/fastweb-http-server/routes.js.map +0 -1
  88. package/dist/contrib/fastweb-http-server/tool-schemas.d.ts +0 -63
  89. package/dist/contrib/fastweb-http-server/tool-schemas.d.ts.map +0 -1
  90. package/dist/contrib/fastweb-http-server/tool-schemas.js +0 -61
  91. package/dist/contrib/fastweb-http-server/tool-schemas.js.map +0 -1
  92. package/dist/fastbrowser_mcp/libs/mcp_client.d.ts +0 -120
  93. package/dist/fastbrowser_mcp/libs/mcp_client.d.ts.map +0 -1
  94. package/dist/fastbrowser_mcp/libs/mcp_client.js +0 -83
  95. package/dist/fastbrowser_mcp/libs/mcp_client.js.map +0 -1
  96. package/dist/fastweb_cli/fastweb_cli.d.ts +0 -3
  97. package/dist/fastweb_cli/fastweb_cli.d.ts.map +0 -1
  98. package/dist/fastweb_cli/fastweb_cli.js +0 -254
  99. package/dist/fastweb_cli/fastweb_cli.js.map +0 -1
  100. package/dist/fastweb_cli/http-client.d.ts +0 -7
  101. package/dist/fastweb_cli/http-client.d.ts.map +0 -1
  102. package/dist/fastweb_cli/http-client.js +0 -51
  103. package/dist/fastweb_cli/http-client.js.map +0 -1
  104. package/dist/fastweb_cli/libs/http-client.d.ts +0 -7
  105. package/dist/fastweb_cli/libs/http-client.d.ts.map +0 -1
  106. package/dist/fastweb_cli/libs/http-client.js +0 -51
  107. package/dist/fastweb_cli/libs/http-client.js.map +0 -1
  108. package/dist/fastweb_cli/libs/server-manager.d.ts +0 -12
  109. package/dist/fastweb_cli/libs/server-manager.d.ts.map +0 -1
  110. package/dist/fastweb_cli/libs/server-manager.js +0 -194
  111. package/dist/fastweb_cli/libs/server-manager.js.map +0 -1
  112. package/dist/fastweb_http_server/fastweb_http_server.d.ts +0 -3
  113. package/dist/fastweb_http_server/fastweb_http_server.d.ts.map +0 -1
  114. package/dist/fastweb_http_server/fastweb_http_server.js +0 -82
  115. package/dist/fastweb_http_server/fastweb_http_server.js.map +0 -1
  116. package/dist/fastweb_http_server/libs/routes.d.ts +0 -6
  117. package/dist/fastweb_http_server/libs/routes.d.ts.map +0 -1
  118. package/dist/fastweb_http_server/libs/routes.js +0 -41
  119. package/dist/fastweb_http_server/libs/routes.js.map +0 -1
  120. package/dist/fastweb_http_server/libs/tool-schemas.d.ts +0 -72
  121. package/dist/fastweb_http_server/libs/tool-schemas.d.ts.map +0 -1
  122. package/dist/fastweb_http_server/libs/tool-schemas.js +0 -65
  123. package/dist/fastweb_http_server/libs/tool-schemas.js.map +0 -1
  124. package/dist/fastweb_http_server/routes.d.ts +0 -6
  125. package/dist/fastweb_http_server/routes.d.ts.map +0 -1
  126. package/dist/fastweb_http_server/routes.js +0 -41
  127. package/dist/fastweb_http_server/routes.js.map +0 -1
  128. package/dist/fastweb_http_server/tool-schemas.d.ts +0 -63
  129. package/dist/fastweb_http_server/tool-schemas.d.ts.map +0 -1
  130. package/dist/fastweb_http_server/tool-schemas.js +0 -61
  131. package/dist/fastweb_http_server/tool-schemas.js.map +0 -1
  132. package/dist/fastweb_mcp/fastweb_mcp.d.ts +0 -4
  133. package/dist/fastweb_mcp/fastweb_mcp.d.ts.map +0 -1
  134. package/dist/fastweb_mcp/fastweb_mcp.js +0 -417
  135. package/dist/fastweb_mcp/fastweb_mcp.js.map +0 -1
  136. package/dist/fastweb_mcp/libs/mcp_client.d.ts.map +0 -1
  137. package/dist/fastweb_mcp/libs/mcp_client.js.map +0 -1
  138. package/dist/fastweb_mcp/libs/mcp_proxy.d.ts +0 -10
  139. package/dist/fastweb_mcp/libs/mcp_proxy.d.ts.map +0 -1
  140. package/dist/fastweb_mcp/libs/mcp_proxy.js +0 -45
  141. package/dist/fastweb_mcp/libs/mcp_proxy.js.map +0 -1
  142. package/dist/fastweb_mcp/libs/schemas.d.ts +0 -28
  143. package/dist/fastweb_mcp/libs/schemas.d.ts.map +0 -1
  144. package/dist/fastweb_mcp/libs/schemas.js +0 -38
  145. package/dist/fastweb_mcp/libs/schemas.js.map +0 -1
  146. package/dist/src/fastweb_mcp.d.ts +0 -17
  147. package/dist/src/fastweb_mcp.d.ts.map +0 -1
  148. package/dist/src/fastweb_mcp.js +0 -342
  149. package/dist/src/fastweb_mcp.js.map +0 -1
  150. package/dist/src/libs/mcp_client.d.ts.map +0 -1
  151. package/dist/src/libs/mcp_client.js.map +0 -1
  152. package/dist/src/libs/mcp_proxy.d.ts +0 -10
  153. package/dist/src/libs/mcp_proxy.d.ts.map +0 -1
  154. package/dist/src/libs/mcp_proxy.js +0 -45
  155. package/dist/src/libs/mcp_proxy.js.map +0 -1
  156. package/tmp/dotclaude/skills/fastbrowser/SKILL.md +0 -214
@@ -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,20 +1,25 @@
1
1
  {
2
2
  "name": "fastbrowser_cli",
3
- "version": "1.0.13",
3
+ "version": "1.0.16",
4
4
  "description": "",
5
5
  "main": "dist/fastbrowser_cli/fastbrowser_cli.js",
6
6
  "bin": {
7
- "fastbrowser_cli": "./dist/fastbrowser_cli/fastbrowser_cli.js"
7
+ "fastbrowser_cli": "./dist/fastbrowser_cli/fastbrowser_cli.js",
8
+ "fastbrowser_mcp": "./dist/fastbrowser_mcp/fastbrowser_mcp.js"
8
9
  },
9
10
  "scripts": {
10
11
  "start:fastbrowser_mcp": "npx tsx ./src/fastbrowser_mcp/fastbrowser_mcp.ts",
11
- "start:http-server": "npx tsx ./src/fastbrowser_httpd/fastbrowser_httpd.ts",
12
+ "start:httpd": "npx tsx ./src/fastbrowser_httpd/fastbrowser_httpd.ts",
12
13
  "start:cli": "npx tsx ./src/fastbrowser_cli/fastbrowser_cli.ts",
13
- "inspect:fastbrowser_mcp": "npx @modelcontextprotocol/inspector npx tsx ./src/fastbrowser_mcp/fastbrowser_mcp.ts mcp_server",
14
- "inspect:chrome-devtools": "npx @modelcontextprotocol/inspector npx chrome-devtools-mcp@latest --autoconnect",
15
- "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",
16
21
  "publish:all": "npm run build && npm version patch && npm publish --access public",
17
- "typecheck": "tsc --noEmit"
22
+ "typecheck": "tsc -p tsconfig.json --noEmit"
18
23
  },
19
24
  "keywords": [],
20
25
  "author": "",
@@ -24,10 +29,10 @@
24
29
  "url": "https://github.com/jeromeetienne/skillmd_collecetion.git"
25
30
  },
26
31
  "homepage": "https://github.com/jeromeetienne/skillmd_collection/packages/fastbrowser_cli#readme",
27
- "type": "commonjs",
32
+ "type": "module",
28
33
  "dependencies": {
29
34
  "@modelcontextprotocol/sdk": "^1.29.0",
30
- "a11y_parse": "^1.0.1",
35
+ "a11y_parse": "file:../a11y_parse",
31
36
  "commander": "^12.1.0",
32
37
  "express": "^4.21.2",
33
38
  "string-argv": "^0.3.2",
@@ -183,7 +183,7 @@ npx fastbrowser_cli click --selector "#1_42"
183
183
  npx fastbrowser_cli click -s 'button[name="Submit"]'
184
184
 
185
185
  # Fill a single form field - selector can be a uid (#1_7) or any CSS-like selector
186
- npx fastbrowser_cli fill_form -s 'textbox[name="Email"]' --value "hello@example.com"
186
+ npx fastbrowser_cli fill_form -s 'textbox[name="Email"]' -v "hello@example.com"
187
187
 
188
188
  # Press a comma-separated sequence of keys (literals and named keys both work)
189
189
  npx fastbrowser_cli press_keys --keys "Tab, Tab, Enter"
@@ -221,7 +221,7 @@ Example `demo.fbs`:
221
221
  ```
222
222
  # open a page and interact
223
223
  new_page --url https://example.com
224
- fill_form -s 'textbox[name="Email"]' --value 'hello@example.com'
224
+ fill_form -s 'textbox[name="Email"]' -v 'hello@example.com'
225
225
  press_keys --keys "Tab, Enter"
226
226
  ```
227
227
 
@@ -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
  });