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.
- package/README.md +55 -10
- package/dist/fastbrowser_cli/fastbrowser_cli.js +40 -70
- package/dist/fastbrowser_cli/fastbrowser_cli.js.map +1 -1
- package/dist/fastbrowser_cli/libs/query-builder.d.ts +17 -0
- package/dist/fastbrowser_cli/libs/query-builder.d.ts.map +1 -0
- package/dist/fastbrowser_cli/libs/query-builder.js +58 -0
- package/dist/fastbrowser_cli/libs/query-builder.js.map +1 -0
- package/dist/fastbrowser_cli/libs/server-manager.d.ts +8 -3
- package/dist/fastbrowser_cli/libs/server-manager.d.ts.map +1 -1
- package/dist/fastbrowser_cli/libs/server-manager.js +30 -15
- package/dist/fastbrowser_cli/libs/server-manager.js.map +1 -1
- package/dist/fastbrowser_httpd/fastbrowser_httpd.js +9 -6
- package/dist/fastbrowser_httpd/fastbrowser_httpd.js.map +1 -1
- package/dist/fastbrowser_httpd/libs/routes.d.ts +2 -1
- package/dist/fastbrowser_httpd/libs/routes.d.ts.map +1 -1
- package/dist/fastbrowser_httpd/libs/routes.js +2 -2
- package/dist/fastbrowser_httpd/libs/routes.js.map +1 -1
- package/docs/articles/ai_workflow_to_shell_scripts.md +74 -16
- package/docs/articles/ai_workflow_to_shell_scripts.notes.md +23 -0
- package/docs/articles/ai_workflow_to_shell_scripts.outline.md +64 -0
- package/docs/publishing_workflow.md +136 -0
- package/examples/post-to-x.sh +3 -0
- package/examples/welcometothejungle/fastbrowser_helper.ts +39 -0
- package/examples/welcometothejungle/wttj-job.ts +132 -153
- package/examples/welcometothejungle/wttj-search.ts +61 -84
- package/package.json +48 -47
- package/src/fastbrowser_cli/fastbrowser_cli.ts +50 -90
- package/src/fastbrowser_cli/libs/query-builder.ts +89 -0
- package/src/fastbrowser_cli/libs/server-manager.ts +45 -17
- package/src/fastbrowser_httpd/fastbrowser_httpd.ts +14 -6
- package/src/fastbrowser_httpd/libs/routes.ts +3 -2
- package/tests/http-client.test.ts +63 -0
- package/tests/query-builder.test.ts +204 -0
- package/tests/server-manager.test.ts +124 -0
- 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 {
|
|
4
|
+
import { FastBrowserHelper } from "./fastbrowser_helper.js";
|
|
5
5
|
|
|
6
6
|
///////////////////////////////////////////////////////////////////////////////
|
|
7
7
|
|
|
8
8
|
const ALL_STOPS = [
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
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
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
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
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
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
|
-
|
|
174
|
-
|
|
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
|
-
|
|
179
|
-
|
|
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
|
-
|
|
3
|
+
// npm imports
|
|
4
4
|
import { Command, Option } from 'commander';
|
|
5
|
+
import { FastBrowserHelper } from './fastbrowser_helper.js';
|
|
5
6
|
|
|
6
7
|
///////////////////////////////////////////////////////////////////////////////
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
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
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
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
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
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
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
"
|
|
25
|
-
"
|
|
26
|
-
"
|
|
27
|
-
"
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
"
|
|
32
|
-
"
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
"
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
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
|
+
}
|