fastbrowser_cli 1.0.9 → 1.0.12

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 CHANGED
@@ -5,6 +5,8 @@ Control a live Chrome browser from the command line: navigate, click, fill forms
5
5
  A lighter alternative to Chrome DevTools MCP or Puppeteer, designed for AI agents and shell workflows. A persistent HTTP daemon keeps an MCP connection to the browser alive so each command incurs minimal latency.
6
6
 
7
7
  - rely on [chrome-devtools-mcp](https://github.com/ChromeDevTools/chrome-devtools-mcp) for robust browser control
8
+ - rely on [a11y_parse](https://github.com/jeromeetienne/skillmd_collection/tree/main/packages/a11y_parse) for accessibility tree parsing and querying.
9
+ Especially a CSS-like selector syntax optimized for the accessibility tree.
8
10
  - expose a curated, efficient toolset optimized for AI agent use cases
9
11
  - minimize the required round-trips and boilerplate to perform common tasks
10
12
  - minimize the output size, thus reducing LLM input size and parsing complexity.
@@ -32,7 +34,7 @@ npm install -g fastbrowser_cli
32
34
  Or install just the SKILL.md into an agent folder so an AI agent can use it:
33
35
 
34
36
  ```bash
35
- npx fastbrowser_cli --install <skill-folder>
37
+ npx fastbrowser_cli install <skill-folder>
36
38
  ```
37
39
 
38
40
  This copies `SKILL.md` to `<skill-folder>/skills/fastbrowser/SKILL.md`.
@@ -64,6 +66,7 @@ npx fastbrowser_cli click -s 'link[name="More information..."]'
64
66
  | `click` | Click an element by accessibility selector | `-s, --selector` |
65
67
  | `fill_form` | Fill a form field by accessibility selector | `-s, --selector`, `--value` |
66
68
  | `press_keys` | Press a comma-separated key sequence | `--keys` |
69
+ | `install [skill-folder]` | Install SKILL.md into `<skill-folder>/skills/fastbrowser` (default: `.`) | — |
67
70
  | `server start` | Start the HTTP daemon | — |
68
71
  | `server status` | Report daemon running/stopped | — |
69
72
  | `server stop` | Stop the HTTP daemon | — |
@@ -4,138 +4,186 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
4
4
  return (mod && mod.__esModule) ? mod : { "default": mod };
5
5
  };
6
6
  Object.defineProperty(exports, "__esModule", { value: true });
7
- // node imports
8
7
  const node_fs_1 = __importDefault(require("node:fs"));
9
8
  const node_path_1 = __importDefault(require("node:path"));
10
- // npm imports
11
9
  const commander_1 = require("commander");
12
- // local imports
10
+ const string_argv_1 = __importDefault(require("string-argv"));
13
11
  const http_client_js_1 = require("./libs/http-client.js");
14
12
  const server_manager_js_1 = require("./libs/server-manager.js");
15
- function getServerFromCmd(cmd) {
16
- const globalOpts = cmd.optsWithGlobals();
17
- return http_client_js_1.HttpClient.getServerUrl(globalOpts.server);
18
- }
19
- function getAutostartFromCmd(cmd) {
20
- const globalOpts = cmd.optsWithGlobals();
21
- return globalOpts.autostart !== false;
13
+ class SilentExitError extends Error {
14
+ constructor() {
15
+ super('silent-exit');
16
+ }
22
17
  }
23
- async function runTool(cmd, routeName, body) {
24
- const server = getServerFromCmd(cmd);
25
- try {
26
- if (getAutostartFromCmd(cmd) === true) {
18
+ ///////////////////////////////////////////////////////////////////////////////
19
+ class MainHelper {
20
+ static getServerFromCmd(cmd) {
21
+ const globalOpts = cmd.optsWithGlobals();
22
+ return http_client_js_1.HttpClient.getServerUrl(globalOpts.server);
23
+ }
24
+ static getAutostartFromCmd(cmd) {
25
+ const globalOpts = cmd.optsWithGlobals();
26
+ return globalOpts.autostart !== false;
27
+ }
28
+ static async runTool(cmd, routeName, body) {
29
+ const server = MainHelper.getServerFromCmd(cmd);
30
+ if (MainHelper.getAutostartFromCmd(cmd) === true) {
27
31
  await server_manager_js_1.ServerManager.ensureRunning(server);
28
32
  }
29
33
  const response = await http_client_js_1.HttpClient.postTool(server, routeName, body);
30
34
  http_client_js_1.HttpClient.printResponse(response);
31
35
  }
32
- catch (err) {
33
- const message = err instanceof Error ? err.message : String(err);
34
- console.error(`fastbrowser-cli error: ${message}`);
35
- process.exit(1);
36
- }
37
- }
38
- ///////////////////////////////////////////////////////////////////////////////
39
- ///////////////////////////////////////////////////////////////////////////////
40
- //
41
- ///////////////////////////////////////////////////////////////////////////////
42
- ///////////////////////////////////////////////////////////////////////////////
43
- function buildQuerySelectorsBody(opts) {
44
- if (opts.selectorsJson !== undefined && opts.selectorsJson !== '') {
45
- let parsed;
46
- try {
47
- parsed = JSON.parse(opts.selectorsJson);
36
+ static buildQuerySelectorsBody(opts) {
37
+ if (opts.selectorsJson !== undefined && opts.selectorsJson !== '') {
38
+ let parsed;
39
+ try {
40
+ parsed = JSON.parse(opts.selectorsJson);
41
+ }
42
+ catch (err) {
43
+ throw new Error(`--selectors-json is not valid JSON: ${err.message}`);
44
+ }
45
+ if (Array.isArray(parsed) === false) {
46
+ throw new Error('--selectors-json must be a JSON array');
47
+ }
48
+ return { selectors: parsed };
48
49
  }
49
- catch (err) {
50
- throw new Error(`--selectors-json is not valid JSON: ${err.message}`);
50
+ const selectorList = opts.selector ?? [];
51
+ if (selectorList.length === 0) {
52
+ throw new Error('At least one --selector or --selectors-json is required');
51
53
  }
52
- if (Array.isArray(parsed) === false) {
53
- throw new Error('--selectors-json must be a JSON array');
54
+ const limit = opts.limit === undefined ? 0 : Number.parseInt(opts.limit, 10);
55
+ if (Number.isNaN(limit) === true) {
56
+ throw new Error(`Invalid --limit: ${opts.limit}`);
54
57
  }
55
- return { selectors: parsed };
58
+ const withAncestors = opts.withAncestors !== false;
59
+ const selectors = selectorList.map((selector) => ({
60
+ selector,
61
+ limit,
62
+ withAncestors,
63
+ }));
64
+ return { selectors };
56
65
  }
57
- const selectorList = opts.selector ?? [];
58
- if (selectorList.length === 0) {
59
- throw new Error('At least one --selector or --selectors-json is required');
60
- }
61
- const limit = opts.limit === undefined ? 0 : Number.parseInt(opts.limit, 10);
62
- if (Number.isNaN(limit) === true) {
63
- throw new Error(`Invalid --limit: ${opts.limit}`);
66
+ static buildQuerySelectorFirstBody(opts) {
67
+ if (opts.selectorsJson !== undefined && opts.selectorsJson !== '') {
68
+ let parsed;
69
+ try {
70
+ parsed = JSON.parse(opts.selectorsJson);
71
+ }
72
+ catch (err) {
73
+ throw new Error(`--selectors-json is not valid JSON: ${err.message}`);
74
+ }
75
+ if (Array.isArray(parsed) === false) {
76
+ throw new Error('--selectors-json must be a JSON array');
77
+ }
78
+ return { selectors: parsed };
79
+ }
80
+ const selectorList = opts.selector ?? [];
81
+ if (selectorList.length === 0) {
82
+ throw new Error('At least one --selector or --selectors-json is required');
83
+ }
84
+ const withAncestors = opts.withAncestors !== false;
85
+ const selectors = selectorList.map((selector) => ({
86
+ selector,
87
+ withAncestors,
88
+ }));
89
+ return { selectors };
64
90
  }
65
- const withAncestors = opts.withAncestors !== false;
66
- const selectors = selectorList.map((selector) => ({
67
- selector,
68
- limit,
69
- withAncestors,
70
- }));
71
- return { selectors };
72
- }
73
- function buildQuerySelectorFirstBody(opts) {
74
- if (opts.selectorsJson !== undefined && opts.selectorsJson !== '') {
75
- let parsed;
91
+ static async runInstall(skillFolder) {
92
+ const sourceSkillMd = node_path_1.default.resolve(__dirname, '../../skills/fastbrowser/SKILL.md');
93
+ const targetDir = node_path_1.default.resolve(skillFolder, 'skills', 'fastbrowser');
94
+ const targetSkillMd = node_path_1.default.join(targetDir, 'SKILL.md');
76
95
  try {
77
- parsed = JSON.parse(opts.selectorsJson);
96
+ await node_fs_1.default.promises.mkdir(targetDir, { recursive: true });
97
+ await node_fs_1.default.promises.copyFile(sourceSkillMd, targetSkillMd);
98
+ console.log(`Installed fastbrowser SKILL.md at ${targetSkillMd}`);
78
99
  }
79
100
  catch (err) {
80
- throw new Error(`--selectors-json is not valid JSON: ${err.message}`);
81
- }
82
- if (Array.isArray(parsed) === false) {
83
- throw new Error('--selectors-json must be a JSON array');
101
+ const message = err instanceof Error ? err.message : String(err);
102
+ console.error(`fastbrowser-cli error: ${message}`);
103
+ process.exit(1);
84
104
  }
85
- return { selectors: parsed };
86
105
  }
87
- const selectorList = opts.selector ?? [];
88
- if (selectorList.length === 0) {
89
- throw new Error('At least one --selector or --selectors-json is required');
106
+ static async readBatchSource(file, inlineScript) {
107
+ if (inlineScript !== undefined && inlineScript !== '') {
108
+ return inlineScript;
109
+ }
110
+ if (file !== undefined && file !== '') {
111
+ return await node_fs_1.default.promises.readFile(file, 'utf-8');
112
+ }
113
+ if (process.stdin.isTTY === true) {
114
+ throw new Error('batch: no input. Provide a file path, --script, or pipe commands on stdin.');
115
+ }
116
+ return await MainHelper.readStdinToString();
90
117
  }
91
- const withAncestors = opts.withAncestors !== false;
92
- const selectors = selectorList.map((selector) => ({
93
- selector,
94
- withAncestors,
95
- }));
96
- return { selectors };
97
- }
98
- ///////////////////////////////////////////////////////////////////////////////
99
- ///////////////////////////////////////////////////////////////////////////////
100
- //
101
- ///////////////////////////////////////////////////////////////////////////////
102
- ///////////////////////////////////////////////////////////////////////////////
103
- async function runInstall(skillFolder) {
104
- const sourceSkillMd = node_path_1.default.resolve(__dirname, '../../skills/fastbrowser/SKILL.md');
105
- const targetDir = node_path_1.default.resolve(skillFolder, 'skills', 'fastbrowser');
106
- const targetSkillMd = node_path_1.default.join(targetDir, 'SKILL.md');
107
- try {
108
- await node_fs_1.default.promises.mkdir(targetDir, { recursive: true });
109
- await node_fs_1.default.promises.copyFile(sourceSkillMd, targetSkillMd);
110
- console.log(`Installed fastbrowser SKILL.md at ${targetSkillMd}`);
118
+ static async readStdinToString() {
119
+ const chunks = [];
120
+ return await new Promise((resolve, reject) => {
121
+ process.stdin.on('data', (chunk) => chunks.push(chunk));
122
+ process.stdin.on('end', () => resolve(Buffer.concat(chunks).toString('utf-8')));
123
+ process.stdin.on('error', (err) => reject(err));
124
+ });
111
125
  }
112
- catch (err) {
113
- const message = err instanceof Error ? err.message : String(err);
114
- console.error(`fastbrowser-cli error: ${message}`);
115
- process.exit(1);
126
+ static async runBatch(program, source, stopOnError, batchCmd) {
127
+ const globalOpts = batchCmd.optsWithGlobals();
128
+ const globalFlags = [];
129
+ if (globalOpts.server !== undefined) {
130
+ globalFlags.push('--server', globalOpts.server);
131
+ }
132
+ if (globalOpts.autostart === false) {
133
+ globalFlags.push('--no-autostart');
134
+ }
135
+ const lines = source.split('\n');
136
+ let ok = 0;
137
+ let failed = 0;
138
+ for (const rawLine of lines) {
139
+ const line = rawLine.trim();
140
+ if (line === '' || line.startsWith('#') === true)
141
+ continue;
142
+ const argv = (0, string_argv_1.default)(line);
143
+ if (argv.length === 0)
144
+ continue;
145
+ console.log(`> ${line}`);
146
+ try {
147
+ await program.parseAsync([...globalFlags, ...argv], { from: 'user' });
148
+ ok += 1;
149
+ }
150
+ catch (err) {
151
+ failed += 1;
152
+ if (err instanceof SilentExitError === false && err instanceof commander_1.CommanderError === false) {
153
+ const message = err instanceof Error ? err.message : String(err);
154
+ console.error(`fastbrowser-cli error: ${message}`);
155
+ }
156
+ if (stopOnError === true) {
157
+ throw new SilentExitError();
158
+ }
159
+ }
160
+ }
161
+ if (stopOnError === false) {
162
+ console.log(`batch: ${ok} ok, ${failed} failed`);
163
+ }
164
+ if (failed > 0) {
165
+ throw new SilentExitError();
166
+ }
116
167
  }
117
168
  }
118
169
  ///////////////////////////////////////////////////////////////////////////////
119
170
  ///////////////////////////////////////////////////////////////////////////////
120
- //
171
+ //
121
172
  ///////////////////////////////////////////////////////////////////////////////
122
173
  ///////////////////////////////////////////////////////////////////////////////
123
174
  async function main() {
124
- const installIdx = process.argv.indexOf('--install');
125
- if (installIdx !== -1) {
126
- const next = process.argv[installIdx + 1];
127
- const skillFolder = (next !== undefined && next.startsWith('-') === false) ? next : '.';
128
- await runInstall(skillFolder);
129
- return;
130
- }
131
175
  const program = new commander_1.Command();
132
176
  program
133
177
  .name('fastbrowser-cli')
134
178
  .description('CLI client for fastbrowser')
135
179
  .option('--server <url>', 'fastbrowser-httpd URL (default: env FASTBROWSER_SERVER or http://localhost:8787)')
136
180
  .option('--autostart', 'Auto-start the server before a command if it is not running', true)
137
- .option('--no-autostart', 'Do not auto-start the server before a command')
138
- .option('--install [skill-folder]', 'Install SKILL.md into <skill-folder>/skills/fastbrowser (default: .)');
181
+ .option('--no-autostart', 'Do not auto-start the server before a command');
182
+ ///////////////////////////////////////////////////////////////////////////////
183
+ ///////////////////////////////////////////////////////////////////////////////
184
+ //
185
+ ///////////////////////////////////////////////////////////////////////////////
186
+ ///////////////////////////////////////////////////////////////////////////////
139
187
  const serverCmd = program
140
188
  .command('server')
141
189
  .description('Manage the fastbrowser HTTP server');
@@ -143,52 +191,43 @@ async function main() {
143
191
  .command('start')
144
192
  .description('Start the fastbrowser HTTP server as a detached daemon')
145
193
  .action(async (_opts, cmd) => {
146
- const server = getServerFromCmd(cmd);
147
- try {
148
- await server_manager_js_1.ServerManager.start(server);
149
- }
150
- catch (err) {
151
- const message = err instanceof Error ? err.message : String(err);
152
- console.error(`fastbrowser-cli error: ${message}`);
153
- process.exit(1);
154
- }
194
+ const server = MainHelper.getServerFromCmd(cmd);
195
+ await server_manager_js_1.ServerManager.start(server);
155
196
  });
156
197
  serverCmd
157
198
  .command('stop')
158
199
  .description('Stop the fastbrowser HTTP server')
159
200
  .action(async (_opts, cmd) => {
160
- const server = getServerFromCmd(cmd);
161
- try {
162
- await server_manager_js_1.ServerManager.stop(server);
163
- }
164
- catch (err) {
165
- const message = err instanceof Error ? err.message : String(err);
166
- console.error(`fastbrowser-cli error: ${message}`);
167
- process.exit(1);
168
- }
201
+ const server = MainHelper.getServerFromCmd(cmd);
202
+ await server_manager_js_1.ServerManager.stop(server);
169
203
  });
170
204
  serverCmd
171
205
  .command('status')
172
206
  .description('Report whether the fastbrowser HTTP server is running')
173
207
  .action(async (_opts, cmd) => {
174
- const server = getServerFromCmd(cmd);
208
+ const server = MainHelper.getServerFromCmd(cmd);
175
209
  const state = await server_manager_js_1.ServerManager.status(server);
176
210
  console.log(`fastbrowser server at ${server}: ${state}`);
177
211
  if (state === 'stopped')
178
- process.exit(1);
212
+ throw new SilentExitError();
179
213
  });
214
+ ///////////////////////////////////////////////////////////////////////////////
215
+ ///////////////////////////////////////////////////////////////////////////////
216
+ //
217
+ ///////////////////////////////////////////////////////////////////////////////
218
+ ///////////////////////////////////////////////////////////////////////////////
180
219
  program
181
220
  .command('list_pages')
182
221
  .description('List all open browser pages')
183
222
  .action(async (_opts, cmd) => {
184
- await runTool(cmd, 'list_pages', {});
223
+ await MainHelper.runTool(cmd, 'list_pages', {});
185
224
  });
186
225
  program
187
226
  .command('new_page')
188
227
  .description('Open a new browser page')
189
228
  .requiredOption('--url <url>', 'URL to open')
190
229
  .action(async (opts, cmd) => {
191
- await runTool(cmd, 'new_page', { url: opts.url });
230
+ await MainHelper.runTool(cmd, 'new_page', { url: opts.url });
192
231
  });
193
232
  program
194
233
  .command('close_page')
@@ -197,32 +236,31 @@ async function main() {
197
236
  .action(async (opts, cmd) => {
198
237
  const pageId = Number.parseInt(opts.pageId, 10);
199
238
  if (Number.isNaN(pageId) === true) {
200
- console.error(`Invalid --page-id: ${opts.pageId}`);
201
- process.exit(1);
239
+ throw new Error(`Invalid --page-id: ${opts.pageId}`);
202
240
  }
203
- await runTool(cmd, 'close_page', { pageId });
241
+ await MainHelper.runTool(cmd, 'close_page', { pageId });
204
242
  });
205
243
  program
206
244
  .command('navigate_page')
207
245
  .description('Navigate the current page to a URL')
208
246
  .requiredOption('--url <url>', 'URL to navigate to')
209
247
  .action(async (opts, cmd) => {
210
- await runTool(cmd, 'navigate_page', { url: opts.url });
248
+ await MainHelper.runTool(cmd, 'navigate_page', { url: opts.url });
211
249
  });
212
250
  program
213
251
  .command('click')
214
252
  .description('Click an element by its accessibility selector')
215
253
  .requiredOption('-s, --selector <selector>', 'Accessibility selector (e.g. "#1_3" or \'button[name="Submit"]\')')
216
254
  .action(async (opts, cmd) => {
217
- await runTool(cmd, 'click', { selector: opts.selector });
255
+ await MainHelper.runTool(cmd, 'click', { selector: opts.selector });
218
256
  });
219
257
  program
220
258
  .command('fill_form')
221
259
  .description('Fill a form field by its accessibility selector')
222
260
  .requiredOption('-s, --selector <selector>', 'Accessibility selector (e.g. "#1_3" or \'textbox[name="Email"]\')')
223
- .requiredOption('--value <value>', 'Value to fill')
261
+ .requiredOption('-v, --value <value>', 'Value to fill')
224
262
  .action(async (opts, cmd) => {
225
- await runTool(cmd, 'fill_form', {
263
+ await MainHelper.runTool(cmd, 'fill_form', {
226
264
  elements: [{ selector: opts.selector, value: opts.value }],
227
265
  });
228
266
  });
@@ -238,15 +276,8 @@ async function main() {
238
276
  .option('--no-with-ancestors', 'Exclude ancestor nodes')
239
277
  .option('--selectors-json <json>', 'JSON array of {selector,limit,withAncestors} for per-selector control')
240
278
  .action(async (opts, cmd) => {
241
- let body;
242
- try {
243
- body = buildQuerySelectorsBody(opts);
244
- }
245
- catch (err) {
246
- console.error(`fastbrowser-cli error: ${err.message}`);
247
- process.exit(1);
248
- }
249
- await runTool(cmd, 'query_selectors_all', body);
279
+ const body = MainHelper.buildQuerySelectorsBody(opts);
280
+ await MainHelper.runTool(cmd, 'query_selectors_all', body);
250
281
  });
251
282
  program
252
283
  .command('query_selectors')
@@ -259,30 +290,43 @@ async function main() {
259
290
  .option('--no-with-ancestors', 'Exclude ancestor nodes')
260
291
  .option('--selectors-json <json>', 'JSON array of {selector,withAncestors} for per-selector control')
261
292
  .action(async (opts, cmd) => {
262
- let body;
263
- try {
264
- body = buildQuerySelectorFirstBody(opts);
265
- }
266
- catch (err) {
267
- console.error(`fastbrowser-cli error: ${err.message}`);
268
- process.exit(1);
269
- }
270
- await runTool(cmd, 'query_selectors', body);
293
+ const body = MainHelper.buildQuerySelectorFirstBody(opts);
294
+ await MainHelper.runTool(cmd, 'query_selectors', body);
271
295
  });
272
296
  program
273
297
  .command('take_snapshot')
274
298
  .description('Take an accessibility-tree snapshot of the current page')
275
299
  .action(async (_opts, cmd) => {
276
- await runTool(cmd, 'take_snapshot', {});
300
+ await MainHelper.runTool(cmd, 'take_snapshot', {});
277
301
  });
278
302
  program
279
303
  .command('press_keys')
280
304
  .description('Press a sequence of keys')
281
305
  .requiredOption('--keys <keys>', "Comma-separated keys. E.g. 'Hello, Tab, Enter'")
282
306
  .action(async (opts, cmd) => {
283
- await runTool(cmd, 'press_keys', { keys: opts.keys });
307
+ await MainHelper.runTool(cmd, 'press_keys', { keys: opts.keys });
308
+ });
309
+ program
310
+ .command('install [skill-folder]')
311
+ .description('Install SKILL.md into <skill-folder>/skills/fastbrowser (default: .)')
312
+ .action(async (skillFolder) => {
313
+ await MainHelper.runInstall(skillFolder ?? '.');
314
+ });
315
+ program
316
+ .command('batch [file]')
317
+ .description('Run multiple commands from a file, piped stdin, or an inline --script string (one command per line, # comments allowed)')
318
+ .option('--script <script>', 'Inline multi-line script (overrides [file] and stdin)')
319
+ .option('--no-stop-on-error', 'Continue running subsequent lines after a failure (default: stop on first error)')
320
+ .action(async (file, opts, cmd) => {
321
+ const source = await MainHelper.readBatchSource(file, opts.script);
322
+ await MainHelper.runBatch(program, source, opts.stopOnError !== false, cmd);
284
323
  });
285
- await program.parseAsync(process.argv);
324
+ ///////////////////////////////////////////////////////////////////////////////
325
+ ///////////////////////////////////////////////////////////////////////////////
326
+ //
327
+ ///////////////////////////////////////////////////////////////////////////////
328
+ ///////////////////////////////////////////////////////////////////////////////
329
+ await program.parseAsync();
286
330
  }
287
331
  void main();
288
332
  //# sourceMappingURL=fastbrowser_cli.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"fastbrowser_cli.js","sourceRoot":"","sources":["../../src/fastbrowser_cli/fastbrowser_cli.ts"],"names":[],"mappings":";;;;;;AAEA,eAAe;AACf,sDAAyB;AACzB,0DAA6B;AAE7B,cAAc;AACd,yCAAoC;AAEpC,gBAAgB;AAChB,0DAAmD;AACnD,gEAAyD;AAezD,SAAS,gBAAgB,CAAC,GAAY;IACrC,MAAM,UAAU,GAAG,GAAG,CAAC,eAAe,EAAc,CAAC;IACrD,OAAO,2BAAU,CAAC,YAAY,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC;AACnD,CAAC;AAED,SAAS,mBAAmB,CAAC,GAAY;IACxC,MAAM,UAAU,GAAG,GAAG,CAAC,eAAe,EAAc,CAAC;IACrD,OAAO,UAAU,CAAC,SAAS,KAAK,KAAK,CAAC;AACvC,CAAC;AAED,KAAK,UAAU,OAAO,CAAC,GAAY,EAAE,SAAiB,EAAE,IAAa;IACpE,MAAM,MAAM,GAAG,gBAAgB,CAAC,GAAG,CAAC,CAAC;IACrC,IAAI,CAAC;QACJ,IAAI,mBAAmB,CAAC,GAAG,CAAC,KAAK,IAAI,EAAE,CAAC;YACvC,MAAM,iCAAa,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC;QAC3C,CAAC;QACD,MAAM,QAAQ,GAAG,MAAM,2BAAU,CAAC,QAAQ,CAAC,MAAM,EAAE,SAAS,EAAE,IAAI,CAAC,CAAC;QACpE,2BAAU,CAAC,aAAa,CAAC,QAAQ,CAAC,CAAC;IACpC,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACd,MAAM,OAAO,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QACjE,OAAO,CAAC,KAAK,CAAC,0BAA0B,OAAO,EAAE,CAAC,CAAC;QACnD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACjB,CAAC;AACF,CAAC;AAED,+EAA+E;AAC/E,+EAA+E;AAC/E,EAAE;AACF,+EAA+E;AAC/E,+EAA+E;AAE/E,SAAS,uBAAuB,CAAC,IAKhC;IACA,IAAI,IAAI,CAAC,aAAa,KAAK,SAAS,IAAI,IAAI,CAAC,aAAa,KAAK,EAAE,EAAE,CAAC;QACnE,IAAI,MAAe,CAAC;QACpB,IAAI,CAAC;YACJ,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;QACzC,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACd,MAAM,IAAI,KAAK,CAAC,uCAAwC,GAAa,CAAC,OAAO,EAAE,CAAC,CAAC;QAClF,CAAC;QACD,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,KAAK,EAAE,CAAC;YACrC,MAAM,IAAI,KAAK,CAAC,uCAAuC,CAAC,CAAC;QAC1D,CAAC;QACD,OAAO,EAAE,SAAS,EAAE,MAA8B,EAAE,CAAC;IACtD,CAAC;IAED,MAAM,YAAY,GAAG,IAAI,CAAC,QAAQ,IAAI,EAAE,CAAC;IACzC,IAAI,YAAY,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC/B,MAAM,IAAI,KAAK,CAAC,yDAAyD,CAAC,CAAC;IAC5E,CAAC;IAED,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;IAC7E,IAAI,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,KAAK,IAAI,EAAE,CAAC;QAClC,MAAM,IAAI,KAAK,CAAC,oBAAoB,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC;IACnD,CAAC;IACD,MAAM,aAAa,GAAG,IAAI,CAAC,aAAa,KAAK,KAAK,CAAC;IAEnD,MAAM,SAAS,GAAyB,YAAY,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC;QACvE,QAAQ;QACR,KAAK;QACL,aAAa;KACb,CAAC,CAAC,CAAC;IACJ,OAAO,EAAE,SAAS,EAAE,CAAC;AACtB,CAAC;AAED,SAAS,2BAA2B,CAAC,IAIpC;IACA,IAAI,IAAI,CAAC,aAAa,KAAK,SAAS,IAAI,IAAI,CAAC,aAAa,KAAK,EAAE,EAAE,CAAC;QACnE,IAAI,MAAe,CAAC;QACpB,IAAI,CAAC;YACJ,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;QACzC,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACd,MAAM,IAAI,KAAK,CAAC,uCAAwC,GAAa,CAAC,OAAO,EAAE,CAAC,CAAC;QAClF,CAAC;QACD,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,KAAK,EAAE,CAAC;YACrC,MAAM,IAAI,KAAK,CAAC,uCAAuC,CAAC,CAAC;QAC1D,CAAC;QACD,OAAO,EAAE,SAAS,EAAE,MAAmC,EAAE,CAAC;IAC3D,CAAC;IAED,MAAM,YAAY,GAAG,IAAI,CAAC,QAAQ,IAAI,EAAE,CAAC;IACzC,IAAI,YAAY,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC/B,MAAM,IAAI,KAAK,CAAC,yDAAyD,CAAC,CAAC;IAC5E,CAAC;IAED,MAAM,aAAa,GAAG,IAAI,CAAC,aAAa,KAAK,KAAK,CAAC;IAEnD,MAAM,SAAS,GAA8B,YAAY,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC;QAC5E,QAAQ;QACR,aAAa;KACb,CAAC,CAAC,CAAC;IACJ,OAAO,EAAE,SAAS,EAAE,CAAC;AACtB,CAAC;AAED,+EAA+E;AAC/E,+EAA+E;AAC/E,EAAE;AACF,+EAA+E;AAC/E,+EAA+E;AAE/E,KAAK,UAAU,UAAU,CAAC,WAAmB;IAC5C,MAAM,aAAa,GAAG,mBAAI,CAAC,OAAO,CAAC,SAAS,EAAE,mCAAmC,CAAC,CAAC;IACnF,MAAM,SAAS,GAAG,mBAAI,CAAC,OAAO,CAAC,WAAW,EAAE,QAAQ,EAAE,aAAa,CAAC,CAAC;IACrE,MAAM,aAAa,GAAG,mBAAI,CAAC,IAAI,CAAC,SAAS,EAAE,UAAU,CAAC,CAAC;IACvD,IAAI,CAAC;QACJ,MAAM,iBAAE,CAAC,QAAQ,CAAC,KAAK,CAAC,SAAS,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QACxD,MAAM,iBAAE,CAAC,QAAQ,CAAC,QAAQ,CAAC,aAAa,EAAE,aAAa,CAAC,CAAC;QACzD,OAAO,CAAC,GAAG,CAAC,qCAAqC,aAAa,EAAE,CAAC,CAAC;IACnE,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACd,MAAM,OAAO,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QACjE,OAAO,CAAC,KAAK,CAAC,0BAA0B,OAAO,EAAE,CAAC,CAAC;QACnD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACjB,CAAC;AACF,CAAC;AAED,+EAA+E;AAC/E,+EAA+E;AAC/E,EAAE;AACF,+EAA+E;AAC/E,+EAA+E;AAE/E,KAAK,UAAU,IAAI;IAClB,MAAM,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC;IACrD,IAAI,UAAU,KAAK,CAAC,CAAC,EAAE,CAAC;QACvB,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,UAAU,GAAG,CAAC,CAAC,CAAC;QAC1C,MAAM,WAAW,GAAG,CAAC,IAAI,KAAK,SAAS,IAAI,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC;QACxF,MAAM,UAAU,CAAC,WAAW,CAAC,CAAC;QAC9B,OAAO;IACR,CAAC;IAED,MAAM,OAAO,GAAG,IAAI,mBAAO,EAAE,CAAC;IAC9B,OAAO;SACL,IAAI,CAAC,iBAAiB,CAAC;SACvB,WAAW,CAAC,4BAA4B,CAAC;SACzC,MAAM,CAAC,gBAAgB,EAAE,kFAAkF,CAAC;SAC5G,MAAM,CAAC,aAAa,EAAE,6DAA6D,EAAE,IAAI,CAAC;SAC1F,MAAM,CAAC,gBAAgB,EAAE,+CAA+C,CAAC;SACzE,MAAM,CAAC,0BAA0B,EAAE,sEAAsE,CAAC,CAAC;IAE7G,MAAM,SAAS,GAAG,OAAO;SACvB,OAAO,CAAC,QAAQ,CAAC;SACjB,WAAW,CAAC,oCAAoC,CAAC,CAAC;IAEpD,SAAS;SACP,OAAO,CAAC,OAAO,CAAC;SAChB,WAAW,CAAC,wDAAwD,CAAC;SACrE,MAAM,CAAC,KAAK,EAAE,KAAK,EAAE,GAAY,EAAE,EAAE;QACrC,MAAM,MAAM,GAAG,gBAAgB,CAAC,GAAG,CAAC,CAAC;QACrC,IAAI,CAAC;YACJ,MAAM,iCAAa,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;QACnC,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACd,MAAM,OAAO,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;YACjE,OAAO,CAAC,KAAK,CAAC,0BAA0B,OAAO,EAAE,CAAC,CAAC;YACnD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACjB,CAAC;IACF,CAAC,CAAC,CAAC;IAEJ,SAAS;SACP,OAAO,CAAC,MAAM,CAAC;SACf,WAAW,CAAC,kCAAkC,CAAC;SAC/C,MAAM,CAAC,KAAK,EAAE,KAAK,EAAE,GAAY,EAAE,EAAE;QACrC,MAAM,MAAM,GAAG,gBAAgB,CAAC,GAAG,CAAC,CAAC;QACrC,IAAI,CAAC;YACJ,MAAM,iCAAa,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QAClC,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACd,MAAM,OAAO,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;YACjE,OAAO,CAAC,KAAK,CAAC,0BAA0B,OAAO,EAAE,CAAC,CAAC;YACnD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACjB,CAAC;IACF,CAAC,CAAC,CAAC;IAEJ,SAAS;SACP,OAAO,CAAC,QAAQ,CAAC;SACjB,WAAW,CAAC,uDAAuD,CAAC;SACpE,MAAM,CAAC,KAAK,EAAE,KAAK,EAAE,GAAY,EAAE,EAAE;QACrC,MAAM,MAAM,GAAG,gBAAgB,CAAC,GAAG,CAAC,CAAC;QACrC,MAAM,KAAK,GAAG,MAAM,iCAAa,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;QACjD,OAAO,CAAC,GAAG,CAAC,yBAAyB,MAAM,KAAK,KAAK,EAAE,CAAC,CAAC;QACzD,IAAI,KAAK,KAAK,SAAS;YAAE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAC1C,CAAC,CAAC,CAAC;IAEJ,OAAO;SACL,OAAO,CAAC,YAAY,CAAC;SACrB,WAAW,CAAC,6BAA6B,CAAC;SAC1C,MAAM,CAAC,KAAK,EAAE,KAAK,EAAE,GAAY,EAAE,EAAE;QACrC,MAAM,OAAO,CAAC,GAAG,EAAE,YAAY,EAAE,EAAE,CAAC,CAAC;IACtC,CAAC,CAAC,CAAC;IAEJ,OAAO;SACL,OAAO,CAAC,UAAU,CAAC;SACnB,WAAW,CAAC,yBAAyB,CAAC;SACtC,cAAc,CAAC,aAAa,EAAE,aAAa,CAAC;SAC5C,MAAM,CAAC,KAAK,EAAE,IAAqB,EAAE,GAAY,EAAE,EAAE;QACrD,MAAM,OAAO,CAAC,GAAG,EAAE,UAAU,EAAE,EAAE,GAAG,EAAE,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC;IACnD,CAAC,CAAC,CAAC;IAEJ,OAAO;SACL,OAAO,CAAC,YAAY,CAAC;SACrB,WAAW,CAAC,wBAAwB,CAAC;SACrC,cAAc,CAAC,oBAAoB,EAAE,sBAAsB,CAAC;SAC5D,MAAM,CAAC,KAAK,EAAE,IAAwB,EAAE,GAAY,EAAE,EAAE;QACxD,MAAM,MAAM,GAAG,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;QAChD,IAAI,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,KAAK,IAAI,EAAE,CAAC;YACnC,OAAO,CAAC,KAAK,CAAC,sBAAsB,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC;YACnD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACjB,CAAC;QACD,MAAM,OAAO,CAAC,GAAG,EAAE,YAAY,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC;IAC9C,CAAC,CAAC,CAAC;IAEJ,OAAO;SACL,OAAO,CAAC,eAAe,CAAC;SACxB,WAAW,CAAC,oCAAoC,CAAC;SACjD,cAAc,CAAC,aAAa,EAAE,oBAAoB,CAAC;SACnD,MAAM,CAAC,KAAK,EAAE,IAAqB,EAAE,GAAY,EAAE,EAAE;QACrD,MAAM,OAAO,CAAC,GAAG,EAAE,eAAe,EAAE,EAAE,GAAG,EAAE,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC;IACxD,CAAC,CAAC,CAAC;IAEJ,OAAO;SACL,OAAO,CAAC,OAAO,CAAC;SAChB,WAAW,CAAC,gDAAgD,CAAC;SAC7D,cAAc,CAAC,2BAA2B,EAAE,mEAAmE,CAAC;SAChH,MAAM,CAAC,KAAK,EAAE,IAA0B,EAAE,GAAY,EAAE,EAAE;QAC1D,MAAM,OAAO,CAAC,GAAG,EAAE,OAAO,EAAE,EAAE,QAAQ,EAAE,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC;IAC1D,CAAC,CAAC,CAAC;IAEJ,OAAO;SACL,OAAO,CAAC,WAAW,CAAC;SACpB,WAAW,CAAC,iDAAiD,CAAC;SAC9D,cAAc,CAAC,2BAA2B,EAAE,mEAAmE,CAAC;SAChH,cAAc,CAAC,iBAAiB,EAAE,eAAe,CAAC;SAClD,MAAM,CAAC,KAAK,EAAE,IAAyC,EAAE,GAAY,EAAE,EAAE;QACzE,MAAM,OAAO,CAAC,GAAG,EAAE,WAAW,EAAE;YAC/B,QAAQ,EAAE,CAAC,EAAE,QAAQ,EAAE,IAAI,CAAC,QAAQ,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,CAAC;SAC1D,CAAC,CAAC;IACJ,CAAC,CAAC,CAAC;IAEJ,OAAO;SACL,OAAO,CAAC,qBAAqB,CAAC;SAC9B,WAAW,CAAC,sDAAsD,CAAC;SACnE,MAAM,CAAC,2BAA2B,EAAE,gCAAgC,EAAE,CAAC,KAAa,EAAE,OAAiB,EAAE,EAAE,EAAE;QAC7G,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACjB,OAAO,IAAI,CAAC;IACb,CAAC,CAAC;SACD,MAAM,CAAC,kBAAkB,EAAE,wCAAwC,EAAE,GAAG,CAAC;SACzE,MAAM,CAAC,kBAAkB,EAAE,wBAAwB,EAAE,IAAI,CAAC;SAC1D,MAAM,CAAC,qBAAqB,EAAE,wBAAwB,CAAC;SACvD,MAAM,CAAC,yBAAyB,EAAE,uEAAuE,CAAC;SAC1G,MAAM,CAAC,KAAK,EAAE,IAKd,EAAE,GAAY,EAAE,EAAE;QAClB,IAAI,IAA8B,CAAC;QACnC,IAAI,CAAC;YACJ,IAAI,GAAG,uBAAuB,CAAC,IAAI,CAAC,CAAC;QACtC,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACd,OAAO,CAAC,KAAK,CAAC,0BAA2B,GAAa,CAAC,OAAO,EAAE,CAAC,CAAC;YAClE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACjB,CAAC;QACD,MAAM,OAAO,CAAC,GAAG,EAAE,qBAAqB,EAAE,IAAI,CAAC,CAAC;IACjD,CAAC,CAAC,CAAC;IAEJ,OAAO;SACL,OAAO,CAAC,iBAAiB,CAAC;SAC1B,WAAW,CAAC,oGAAoG,CAAC;SACjH,MAAM,CAAC,2BAA2B,EAAE,gCAAgC,EAAE,CAAC,KAAa,EAAE,OAAiB,EAAE,EAAE,EAAE;QAC7G,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACjB,OAAO,IAAI,CAAC;IACb,CAAC,CAAC;SACD,MAAM,CAAC,kBAAkB,EAAE,wBAAwB,EAAE,IAAI,CAAC;SAC1D,MAAM,CAAC,qBAAqB,EAAE,wBAAwB,CAAC;SACvD,MAAM,CAAC,yBAAyB,EAAE,iEAAiE,CAAC;SACpG,MAAM,CAAC,KAAK,EAAE,IAId,EAAE,GAAY,EAAE,EAAE;QAClB,IAAI,IAA0B,CAAC;QAC/B,IAAI,CAAC;YACJ,IAAI,GAAG,2BAA2B,CAAC,IAAI,CAAC,CAAC;QAC1C,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACd,OAAO,CAAC,KAAK,CAAC,0BAA2B,GAAa,CAAC,OAAO,EAAE,CAAC,CAAC;YAClE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACjB,CAAC;QACD,MAAM,OAAO,CAAC,GAAG,EAAE,iBAAiB,EAAE,IAAI,CAAC,CAAC;IAC7C,CAAC,CAAC,CAAC;IAEJ,OAAO;SACL,OAAO,CAAC,eAAe,CAAC;SACxB,WAAW,CAAC,yDAAyD,CAAC;SACtE,MAAM,CAAC,KAAK,EAAE,KAAK,EAAE,GAAY,EAAE,EAAE;QACrC,MAAM,OAAO,CAAC,GAAG,EAAE,eAAe,EAAE,EAAE,CAAC,CAAC;IACzC,CAAC,CAAC,CAAC;IAEJ,OAAO;SACL,OAAO,CAAC,YAAY,CAAC;SACrB,WAAW,CAAC,0BAA0B,CAAC;SACvC,cAAc,CAAC,eAAe,EAAE,gDAAgD,CAAC;SACjF,MAAM,CAAC,KAAK,EAAE,IAAsB,EAAE,GAAY,EAAE,EAAE;QACtD,MAAM,OAAO,CAAC,GAAG,EAAE,YAAY,EAAE,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;IACvD,CAAC,CAAC,CAAC;IAEJ,MAAM,OAAO,CAAC,UAAU,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;AACxC,CAAC;AAED,KAAK,IAAI,EAAE,CAAC"}
1
+ {"version":3,"file":"fastbrowser_cli.js","sourceRoot":"","sources":["../../src/fastbrowser_cli/fastbrowser_cli.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fastbrowser_cli",
3
- "version": "1.0.9",
3
+ "version": "1.0.12",
4
4
  "description": "",
5
5
  "main": "dist/fastbrowser_cli/fastbrowser_cli.js",
6
6
  "bin": {
@@ -19,12 +19,18 @@
19
19
  "keywords": [],
20
20
  "author": "",
21
21
  "license": "MIT",
22
+ "repository": {
23
+ "type": "git",
24
+ "url": "https://github.com/jeromeetienne/skillmd_collecetion.git"
25
+ },
26
+ "homepage": "https://github.com/jeromeetienne/skillmd_collection/packages/fastbrowser_cli#readme",
22
27
  "type": "commonjs",
23
28
  "dependencies": {
24
29
  "@modelcontextprotocol/sdk": "^1.29.0",
25
30
  "a11y_parse": "^1.0.1",
26
31
  "commander": "^12.1.0",
27
32
  "express": "^4.21.2",
33
+ "string-argv": "^0.3.2",
28
34
  "typescript": "^6.0.3",
29
35
  "zod": "^4.3.6",
30
36
  "zod-from-json-schema": "^0.5.2"
@@ -190,6 +190,41 @@ npx fastbrowser_cli press_keys --keys "Tab, Tab, Enter"
190
190
  npx fastbrowser_cli press_keys --keys "Hello, Tab, Enter"
191
191
  ```
192
192
 
193
+ ## Batch Execution
194
+
195
+ Run several commands from one invocation — each line is parsed and executed like a standalone command, reusing the persistent HTTP server session.
196
+
197
+ Syntax rules:
198
+ - One command per line, same syntax as the standalone CLI (e.g. `click -s '...'`)
199
+ - Shell-style quoting (single and double quotes)
200
+ - Blank lines are ignored
201
+ - Lines starting with `#` are comments
202
+
203
+ By default, the batch stops at the first failing line (shell `set -e` semantics). Pass `--no-stop-on-error` to log failures and continue; the process still exits non-zero at the end if any line failed.
204
+
205
+ ```bash
206
+ # From a file
207
+ npx fastbrowser_cli batch ./demo.fbs
208
+
209
+ # Piped on stdin
210
+ cat demo.fbs | npx fastbrowser_cli batch
211
+
212
+ # Inline script
213
+ npx fastbrowser_cli batch --script $'press_keys --keys "Enter"\nclick -s \'button[name^="Tout effacer"]\''
214
+
215
+ # Continue through failures
216
+ npx fastbrowser_cli batch --no-stop-on-error ./demo.fbs
217
+ ```
218
+
219
+ Example `demo.fbs`:
220
+
221
+ ```
222
+ # open a page and interact
223
+ new_page --url https://example.com
224
+ fill_form -s 'textbox[name="Email"]' --value 'hello@example.com'
225
+ press_keys --keys "Tab, Enter"
226
+ ```
227
+
193
228
  ## Command Reference
194
229
 
195
230
  | Command | Purpose | Required flags |
@@ -204,6 +239,7 @@ npx fastbrowser_cli press_keys --keys "Hello, Tab, Enter"
204
239
  | `click` | Click an element by accessibility selector | `--selector` / `-s` |
205
240
  | `fill_form` | Fill a form field by accessibility selector | `--selector` / `-s`, `--value` |
206
241
  | `press_keys` | Press a comma-separated key sequence | `--keys` |
242
+ | `batch` | Run multiple commands from a file, piped stdin, or `--script` inline | one of: `<file>`, `--script`, or piped stdin |
207
243
  | `server start` | Start the HTTP server daemon | — |
208
244
  | `server status` | Report server running/stopped | — |
209
245
  | `server stop` | Stop the HTTP server | — |
@@ -1,22 +1,15 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- // node imports
4
3
  import fs from 'node:fs';
5
4
  import path from 'node:path';
6
5
 
7
- // npm imports
8
- import { Command } from 'commander';
6
+ import { Command, CommanderError } from 'commander';
7
+ import stringArgv from 'string-argv';
9
8
 
10
- // local imports
11
9
  import { HttpClient } from './libs/http-client.js';
12
10
  import { ServerManager } from './libs/server-manager.js';
13
11
  import type { QuerySelectorInput, QuerySelectorFirstInput, QuerySelectorsAllRequest, QuerySelectorRequest } from '../fastbrowser_httpd/libs/tool-schemas.js';
14
12
 
15
-
16
- ///////////////////////////////////////////////////////////////////////////////
17
- ///////////////////////////////////////////////////////////////////////////////
18
- //
19
- ///////////////////////////////////////////////////////////////////////////////
20
13
  ///////////////////////////////////////////////////////////////////////////////
21
14
 
22
15
  type GlobalOpts = {
@@ -24,151 +17,206 @@ type GlobalOpts = {
24
17
  autostart?: boolean;
25
18
  };
26
19
 
27
- function getServerFromCmd(cmd: Command): string {
28
- const globalOpts = cmd.optsWithGlobals<GlobalOpts>();
29
- return HttpClient.getServerUrl(globalOpts.server);
20
+ class SilentExitError extends Error {
21
+ constructor() {
22
+ super('silent-exit');
23
+ }
30
24
  }
31
25
 
32
- function getAutostartFromCmd(cmd: Command): boolean {
33
- const globalOpts = cmd.optsWithGlobals<GlobalOpts>();
34
- return globalOpts.autostart !== false;
35
- }
26
+ ///////////////////////////////////////////////////////////////////////////////
36
27
 
37
- async function runTool(cmd: Command, routeName: string, body: unknown): Promise<void> {
38
- const server = getServerFromCmd(cmd);
39
- try {
40
- if (getAutostartFromCmd(cmd) === true) {
28
+ class MainHelper {
29
+ static getServerFromCmd(cmd: Command): string {
30
+ const globalOpts = cmd.optsWithGlobals<GlobalOpts>();
31
+ return HttpClient.getServerUrl(globalOpts.server);
32
+ }
33
+
34
+ static getAutostartFromCmd(cmd: Command): boolean {
35
+ const globalOpts = cmd.optsWithGlobals<GlobalOpts>();
36
+ return globalOpts.autostart !== false;
37
+ }
38
+
39
+ static async runTool(cmd: Command, routeName: string, body: unknown): Promise<void> {
40
+ const server = MainHelper.getServerFromCmd(cmd);
41
+ if (MainHelper.getAutostartFromCmd(cmd) === true) {
41
42
  await ServerManager.ensureRunning(server);
42
43
  }
43
44
  const response = await HttpClient.postTool(server, routeName, body);
44
45
  HttpClient.printResponse(response);
45
- } catch (err) {
46
- const message = err instanceof Error ? err.message : String(err);
47
- console.error(`fastbrowser-cli error: ${message}`);
48
- process.exit(1);
49
46
  }
50
- }
51
47
 
52
- ///////////////////////////////////////////////////////////////////////////////
53
- ///////////////////////////////////////////////////////////////////////////////
54
- //
55
- ///////////////////////////////////////////////////////////////////////////////
56
- ///////////////////////////////////////////////////////////////////////////////
48
+ static buildQuerySelectorsBody(opts: {
49
+ selector?: string[];
50
+ limit?: string;
51
+ withAncestors?: boolean;
52
+ selectorsJson?: string;
53
+ }): QuerySelectorsAllRequest {
54
+ if (opts.selectorsJson !== undefined && opts.selectorsJson !== '') {
55
+ let parsed: unknown;
56
+ try {
57
+ parsed = JSON.parse(opts.selectorsJson);
58
+ } catch (err) {
59
+ throw new Error(`--selectors-json is not valid JSON: ${(err as Error).message}`);
60
+ }
61
+ if (Array.isArray(parsed) === false) {
62
+ throw new Error('--selectors-json must be a JSON array');
63
+ }
64
+ return { selectors: parsed as QuerySelectorInput[] };
65
+ }
57
66
 
58
- function buildQuerySelectorsBody(opts: {
59
- selector?: string[];
60
- limit?: string;
61
- withAncestors?: boolean;
62
- selectorsJson?: string;
63
- }): QuerySelectorsAllRequest {
64
- if (opts.selectorsJson !== undefined && opts.selectorsJson !== '') {
65
- let parsed: unknown;
66
- try {
67
- parsed = JSON.parse(opts.selectorsJson);
68
- } catch (err) {
69
- throw new Error(`--selectors-json is not valid JSON: ${(err as Error).message}`);
67
+ const selectorList = opts.selector ?? [];
68
+ if (selectorList.length === 0) {
69
+ throw new Error('At least one --selector or --selectors-json is required');
70
70
  }
71
- if (Array.isArray(parsed) === false) {
72
- throw new Error('--selectors-json must be a JSON array');
71
+
72
+ const limit = opts.limit === undefined ? 0 : Number.parseInt(opts.limit, 10);
73
+ if (Number.isNaN(limit) === true) {
74
+ throw new Error(`Invalid --limit: ${opts.limit}`);
73
75
  }
74
- return { selectors: parsed as QuerySelectorInput[] };
76
+ const withAncestors = opts.withAncestors !== false;
77
+
78
+ const selectors: QuerySelectorInput[] = selectorList.map((selector) => ({
79
+ selector,
80
+ limit,
81
+ withAncestors,
82
+ }));
83
+ return { selectors };
75
84
  }
76
85
 
77
- const selectorList = opts.selector ?? [];
78
- if (selectorList.length === 0) {
79
- throw new Error('At least one --selector or --selectors-json is required');
80
- }
86
+ static buildQuerySelectorFirstBody(opts: {
87
+ selector?: string[];
88
+ withAncestors?: boolean;
89
+ selectorsJson?: string;
90
+ }): QuerySelectorRequest {
91
+ if (opts.selectorsJson !== undefined && opts.selectorsJson !== '') {
92
+ let parsed: unknown;
93
+ try {
94
+ parsed = JSON.parse(opts.selectorsJson);
95
+ } catch (err) {
96
+ throw new Error(`--selectors-json is not valid JSON: ${(err as Error).message}`);
97
+ }
98
+ if (Array.isArray(parsed) === false) {
99
+ throw new Error('--selectors-json must be a JSON array');
100
+ }
101
+ return { selectors: parsed as QuerySelectorFirstInput[] };
102
+ }
103
+
104
+ const selectorList = opts.selector ?? [];
105
+ if (selectorList.length === 0) {
106
+ throw new Error('At least one --selector or --selectors-json is required');
107
+ }
81
108
 
82
- const limit = opts.limit === undefined ? 0 : Number.parseInt(opts.limit, 10);
83
- if (Number.isNaN(limit) === true) {
84
- throw new Error(`Invalid --limit: ${opts.limit}`);
109
+ const withAncestors = opts.withAncestors !== false;
110
+
111
+ const selectors: QuerySelectorFirstInput[] = selectorList.map((selector) => ({
112
+ selector,
113
+ withAncestors,
114
+ }));
115
+ return { selectors };
85
116
  }
86
- const withAncestors = opts.withAncestors !== false;
87
-
88
- const selectors: QuerySelectorInput[] = selectorList.map((selector) => ({
89
- selector,
90
- limit,
91
- withAncestors,
92
- }));
93
- return { selectors };
94
- }
95
117
 
96
- function buildQuerySelectorFirstBody(opts: {
97
- selector?: string[];
98
- withAncestors?: boolean;
99
- selectorsJson?: string;
100
- }): QuerySelectorRequest {
101
- if (opts.selectorsJson !== undefined && opts.selectorsJson !== '') {
102
- let parsed: unknown;
118
+ static async runInstall(skillFolder: string): Promise<void> {
119
+ const sourceSkillMd = path.resolve(__dirname, '../../skills/fastbrowser/SKILL.md');
120
+ const targetDir = path.resolve(skillFolder, 'skills', 'fastbrowser');
121
+ const targetSkillMd = path.join(targetDir, 'SKILL.md');
103
122
  try {
104
- parsed = JSON.parse(opts.selectorsJson);
123
+ await fs.promises.mkdir(targetDir, { recursive: true });
124
+ await fs.promises.copyFile(sourceSkillMd, targetSkillMd);
125
+ console.log(`Installed fastbrowser SKILL.md at ${targetSkillMd}`);
105
126
  } catch (err) {
106
- throw new Error(`--selectors-json is not valid JSON: ${(err as Error).message}`);
127
+ const message = err instanceof Error ? err.message : String(err);
128
+ console.error(`fastbrowser-cli error: ${message}`);
129
+ process.exit(1);
130
+ }
131
+ }
132
+
133
+ static async readBatchSource(file: string | undefined, inlineScript: string | undefined): Promise<string> {
134
+ if (inlineScript !== undefined && inlineScript !== '') {
135
+ return inlineScript;
136
+ }
137
+ if (file !== undefined && file !== '') {
138
+ return await fs.promises.readFile(file, 'utf-8');
107
139
  }
108
- if (Array.isArray(parsed) === false) {
109
- throw new Error('--selectors-json must be a JSON array');
140
+ if (process.stdin.isTTY === true) {
141
+ throw new Error('batch: no input. Provide a file path, --script, or pipe commands on stdin.');
110
142
  }
111
- return { selectors: parsed as QuerySelectorFirstInput[] };
143
+ return await MainHelper.readStdinToString();
112
144
  }
113
145
 
114
- const selectorList = opts.selector ?? [];
115
- if (selectorList.length === 0) {
116
- throw new Error('At least one --selector or --selectors-json is required');
146
+ static async readStdinToString(): Promise<string> {
147
+ const chunks: Buffer[] = [];
148
+ return await new Promise((resolve, reject) => {
149
+ process.stdin.on('data', (chunk) => chunks.push(chunk as Buffer));
150
+ process.stdin.on('end', () => resolve(Buffer.concat(chunks).toString('utf-8')));
151
+ process.stdin.on('error', (err) => reject(err));
152
+ });
117
153
  }
118
154
 
119
- const withAncestors = opts.withAncestors !== false;
155
+ static async runBatch(program: Command, source: string, stopOnError: boolean, batchCmd: Command): Promise<void> {
156
+ const globalOpts = batchCmd.optsWithGlobals<GlobalOpts>();
157
+ const globalFlags: string[] = [];
158
+ if (globalOpts.server !== undefined) {
159
+ globalFlags.push('--server', globalOpts.server);
160
+ }
161
+ if (globalOpts.autostart === false) {
162
+ globalFlags.push('--no-autostart');
163
+ }
120
164
 
121
- const selectors: QuerySelectorFirstInput[] = selectorList.map((selector) => ({
122
- selector,
123
- withAncestors,
124
- }));
125
- return { selectors };
126
- }
165
+ const lines = source.split('\n');
166
+ let ok = 0;
167
+ let failed = 0;
168
+ for (const rawLine of lines) {
169
+ const line = rawLine.trim();
170
+ if (line === '' || line.startsWith('#') === true) continue;
127
171
 
128
- ///////////////////////////////////////////////////////////////////////////////
129
- ///////////////////////////////////////////////////////////////////////////////
130
- //
131
- ///////////////////////////////////////////////////////////////////////////////
132
- ///////////////////////////////////////////////////////////////////////////////
172
+ const argv = stringArgv(line);
173
+ if (argv.length === 0) continue;
133
174
 
134
- async function runInstall(skillFolder: string): Promise<void> {
135
- const sourceSkillMd = path.resolve(__dirname, '../../skills/fastbrowser/SKILL.md');
136
- const targetDir = path.resolve(skillFolder, 'skills', 'fastbrowser');
137
- const targetSkillMd = path.join(targetDir, 'SKILL.md');
138
- try {
139
- await fs.promises.mkdir(targetDir, { recursive: true });
140
- await fs.promises.copyFile(sourceSkillMd, targetSkillMd);
141
- console.log(`Installed fastbrowser SKILL.md at ${targetSkillMd}`);
142
- } catch (err) {
143
- const message = err instanceof Error ? err.message : String(err);
144
- console.error(`fastbrowser-cli error: ${message}`);
145
- process.exit(1);
175
+ console.log(`> ${line}`);
176
+ try {
177
+ await program.parseAsync([...globalFlags, ...argv], { from: 'user' });
178
+ ok += 1;
179
+ } catch (err) {
180
+ failed += 1;
181
+ if (err instanceof SilentExitError === false && err instanceof CommanderError === false) {
182
+ const message = err instanceof Error ? err.message : String(err);
183
+ console.error(`fastbrowser-cli error: ${message}`);
184
+ }
185
+ if (stopOnError === true) {
186
+ throw new SilentExitError();
187
+ }
188
+ }
189
+ }
190
+
191
+ if (stopOnError === false) {
192
+ console.log(`batch: ${ok} ok, ${failed} failed`);
193
+ }
194
+ if (failed > 0) {
195
+ throw new SilentExitError();
196
+ }
146
197
  }
147
198
  }
148
199
 
149
200
  ///////////////////////////////////////////////////////////////////////////////
150
201
  ///////////////////////////////////////////////////////////////////////////////
151
- //
202
+ //
152
203
  ///////////////////////////////////////////////////////////////////////////////
153
204
  ///////////////////////////////////////////////////////////////////////////////
154
205
 
155
206
  async function main(): Promise<void> {
156
- const installIdx = process.argv.indexOf('--install');
157
- if (installIdx !== -1) {
158
- const next = process.argv[installIdx + 1];
159
- const skillFolder = (next !== undefined && next.startsWith('-') === false) ? next : '.';
160
- await runInstall(skillFolder);
161
- return;
162
- }
163
-
164
207
  const program = new Command();
165
208
  program
166
209
  .name('fastbrowser-cli')
167
210
  .description('CLI client for fastbrowser')
168
211
  .option('--server <url>', 'fastbrowser-httpd URL (default: env FASTBROWSER_SERVER or http://localhost:8787)')
169
212
  .option('--autostart', 'Auto-start the server before a command if it is not running', true)
170
- .option('--no-autostart', 'Do not auto-start the server before a command')
171
- .option('--install [skill-folder]', 'Install SKILL.md into <skill-folder>/skills/fastbrowser (default: .)');
213
+ .option('--no-autostart', 'Do not auto-start the server before a command');
214
+
215
+ ///////////////////////////////////////////////////////////////////////////////
216
+ ///////////////////////////////////////////////////////////////////////////////
217
+ //
218
+ ///////////////////////////////////////////////////////////////////////////////
219
+ ///////////////////////////////////////////////////////////////////////////////
172
220
 
173
221
  const serverCmd = program
174
222
  .command('server')
@@ -178,45 +226,39 @@ async function main(): Promise<void> {
178
226
  .command('start')
179
227
  .description('Start the fastbrowser HTTP server as a detached daemon')
180
228
  .action(async (_opts, cmd: Command) => {
181
- const server = getServerFromCmd(cmd);
182
- try {
183
- await ServerManager.start(server);
184
- } catch (err) {
185
- const message = err instanceof Error ? err.message : String(err);
186
- console.error(`fastbrowser-cli error: ${message}`);
187
- process.exit(1);
188
- }
229
+ const server = MainHelper.getServerFromCmd(cmd);
230
+ await ServerManager.start(server);
189
231
  });
190
232
 
191
233
  serverCmd
192
234
  .command('stop')
193
235
  .description('Stop the fastbrowser HTTP server')
194
236
  .action(async (_opts, cmd: Command) => {
195
- const server = getServerFromCmd(cmd);
196
- try {
197
- await ServerManager.stop(server);
198
- } catch (err) {
199
- const message = err instanceof Error ? err.message : String(err);
200
- console.error(`fastbrowser-cli error: ${message}`);
201
- process.exit(1);
202
- }
237
+ const server = MainHelper.getServerFromCmd(cmd);
238
+ await ServerManager.stop(server);
203
239
  });
204
240
 
205
241
  serverCmd
206
242
  .command('status')
207
243
  .description('Report whether the fastbrowser HTTP server is running')
208
244
  .action(async (_opts, cmd: Command) => {
209
- const server = getServerFromCmd(cmd);
245
+ const server = MainHelper.getServerFromCmd(cmd);
210
246
  const state = await ServerManager.status(server);
211
247
  console.log(`fastbrowser server at ${server}: ${state}`);
212
- if (state === 'stopped') process.exit(1);
248
+ if (state === 'stopped') throw new SilentExitError();
213
249
  });
214
250
 
251
+ ///////////////////////////////////////////////////////////////////////////////
252
+ ///////////////////////////////////////////////////////////////////////////////
253
+ //
254
+ ///////////////////////////////////////////////////////////////////////////////
255
+ ///////////////////////////////////////////////////////////////////////////////
256
+
215
257
  program
216
258
  .command('list_pages')
217
259
  .description('List all open browser pages')
218
260
  .action(async (_opts, cmd: Command) => {
219
- await runTool(cmd, 'list_pages', {});
261
+ await MainHelper.runTool(cmd, 'list_pages', {});
220
262
  });
221
263
 
222
264
  program
@@ -224,7 +266,7 @@ async function main(): Promise<void> {
224
266
  .description('Open a new browser page')
225
267
  .requiredOption('--url <url>', 'URL to open')
226
268
  .action(async (opts: { url: string }, cmd: Command) => {
227
- await runTool(cmd, 'new_page', { url: opts.url });
269
+ await MainHelper.runTool(cmd, 'new_page', { url: opts.url });
228
270
  });
229
271
 
230
272
  program
@@ -234,10 +276,9 @@ async function main(): Promise<void> {
234
276
  .action(async (opts: { pageId: string }, cmd: Command) => {
235
277
  const pageId = Number.parseInt(opts.pageId, 10);
236
278
  if (Number.isNaN(pageId) === true) {
237
- console.error(`Invalid --page-id: ${opts.pageId}`);
238
- process.exit(1);
279
+ throw new Error(`Invalid --page-id: ${opts.pageId}`);
239
280
  }
240
- await runTool(cmd, 'close_page', { pageId });
281
+ await MainHelper.runTool(cmd, 'close_page', { pageId });
241
282
  });
242
283
 
243
284
  program
@@ -245,7 +286,7 @@ async function main(): Promise<void> {
245
286
  .description('Navigate the current page to a URL')
246
287
  .requiredOption('--url <url>', 'URL to navigate to')
247
288
  .action(async (opts: { url: string }, cmd: Command) => {
248
- await runTool(cmd, 'navigate_page', { url: opts.url });
289
+ await MainHelper.runTool(cmd, 'navigate_page', { url: opts.url });
249
290
  });
250
291
 
251
292
  program
@@ -253,16 +294,16 @@ async function main(): Promise<void> {
253
294
  .description('Click an element by its accessibility selector')
254
295
  .requiredOption('-s, --selector <selector>', 'Accessibility selector (e.g. "#1_3" or \'button[name="Submit"]\')')
255
296
  .action(async (opts: { selector: string }, cmd: Command) => {
256
- await runTool(cmd, 'click', { selector: opts.selector });
297
+ await MainHelper.runTool(cmd, 'click', { selector: opts.selector });
257
298
  });
258
299
 
259
300
  program
260
301
  .command('fill_form')
261
302
  .description('Fill a form field by its accessibility selector')
262
303
  .requiredOption('-s, --selector <selector>', 'Accessibility selector (e.g. "#1_3" or \'textbox[name="Email"]\')')
263
- .requiredOption('--value <value>', 'Value to fill')
304
+ .requiredOption('-v, --value <value>', 'Value to fill')
264
305
  .action(async (opts: { selector: string; value: string }, cmd: Command) => {
265
- await runTool(cmd, 'fill_form', {
306
+ await MainHelper.runTool(cmd, 'fill_form', {
266
307
  elements: [{ selector: opts.selector, value: opts.value }],
267
308
  });
268
309
  });
@@ -284,14 +325,8 @@ async function main(): Promise<void> {
284
325
  withAncestors?: boolean;
285
326
  selectorsJson?: string;
286
327
  }, cmd: Command) => {
287
- let body: QuerySelectorsAllRequest;
288
- try {
289
- body = buildQuerySelectorsBody(opts);
290
- } catch (err) {
291
- console.error(`fastbrowser-cli error: ${(err as Error).message}`);
292
- process.exit(1);
293
- }
294
- await runTool(cmd, 'query_selectors_all', body);
328
+ const body = MainHelper.buildQuerySelectorsBody(opts);
329
+ await MainHelper.runTool(cmd, 'query_selectors_all', body);
295
330
  });
296
331
 
297
332
  program
@@ -309,21 +344,15 @@ async function main(): Promise<void> {
309
344
  withAncestors?: boolean;
310
345
  selectorsJson?: string;
311
346
  }, cmd: Command) => {
312
- let body: QuerySelectorRequest;
313
- try {
314
- body = buildQuerySelectorFirstBody(opts);
315
- } catch (err) {
316
- console.error(`fastbrowser-cli error: ${(err as Error).message}`);
317
- process.exit(1);
318
- }
319
- await runTool(cmd, 'query_selectors', body);
347
+ const body = MainHelper.buildQuerySelectorFirstBody(opts);
348
+ await MainHelper.runTool(cmd, 'query_selectors', body);
320
349
  });
321
350
 
322
351
  program
323
352
  .command('take_snapshot')
324
353
  .description('Take an accessibility-tree snapshot of the current page')
325
354
  .action(async (_opts, cmd: Command) => {
326
- await runTool(cmd, 'take_snapshot', {});
355
+ await MainHelper.runTool(cmd, 'take_snapshot', {});
327
356
  });
328
357
 
329
358
  program
@@ -331,10 +360,33 @@ async function main(): Promise<void> {
331
360
  .description('Press a sequence of keys')
332
361
  .requiredOption('--keys <keys>', "Comma-separated keys. E.g. 'Hello, Tab, Enter'")
333
362
  .action(async (opts: { keys: string }, cmd: Command) => {
334
- await runTool(cmd, 'press_keys', { keys: opts.keys });
363
+ await MainHelper.runTool(cmd, 'press_keys', { keys: opts.keys });
364
+ });
365
+
366
+ program
367
+ .command('install [skill-folder]')
368
+ .description('Install SKILL.md into <skill-folder>/skills/fastbrowser (default: .)')
369
+ .action(async (skillFolder: string | undefined) => {
370
+ await MainHelper.runInstall(skillFolder ?? '.');
335
371
  });
336
372
 
337
- await program.parseAsync(process.argv);
373
+ program
374
+ .command('batch [file]')
375
+ .description('Run multiple commands from a file, piped stdin, or an inline --script string (one command per line, # comments allowed)')
376
+ .option('--script <script>', 'Inline multi-line script (overrides [file] and stdin)')
377
+ .option('--no-stop-on-error', 'Continue running subsequent lines after a failure (default: stop on first error)')
378
+ .action(async (file: string | undefined, opts: { script?: string; stopOnError: boolean }, cmd: Command) => {
379
+ const source = await MainHelper.readBatchSource(file, opts.script);
380
+ await MainHelper.runBatch(program, source, opts.stopOnError !== false, cmd);
381
+ });
382
+
383
+ ///////////////////////////////////////////////////////////////////////////////
384
+ ///////////////////////////////////////////////////////////////////////////////
385
+ //
386
+ ///////////////////////////////////////////////////////////////////////////////
387
+ ///////////////////////////////////////////////////////////////////////////////
388
+
389
+ await program.parseAsync();
338
390
  }
339
391
 
340
392
  void main();
@@ -1,7 +0,0 @@
1
- {
2
- "permissions": {
3
- "allow": [
4
- "Bash(npx fastbrowser_cli *)"
5
- ]
6
- }
7
- }
@@ -1,7 +0,0 @@
1
- {
2
- "permissions": {
3
- "allow": [
4
- "Bash(npx tsx *)"
5
- ]
6
- }
7
- }