fastbrowser_cli 1.0.31 → 1.0.35

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 (60) hide show
  1. package/README.md +1 -2
  2. package/dist/fastbrowser_cli/fastbrowser_cli.js +11 -19
  3. package/dist/fastbrowser_cli/fastbrowser_cli.js.map +1 -1
  4. package/dist/fastbrowser_cli/libs/query-builder.d.ts +2 -0
  5. package/dist/fastbrowser_cli/libs/query-builder.d.ts.map +1 -1
  6. package/dist/fastbrowser_cli/libs/query-builder.js +4 -0
  7. package/dist/fastbrowser_cli/libs/query-builder.js.map +1 -1
  8. package/dist/fastbrowser_httpd/libs/tool-schemas.d.ts +2 -0
  9. package/dist/fastbrowser_httpd/libs/tool-schemas.d.ts.map +1 -1
  10. package/dist/fastbrowser_mcp/fastbrowser_mcp.js +193 -32
  11. package/dist/fastbrowser_mcp/fastbrowser_mcp.js.map +1 -1
  12. package/dist/fastbrowser_mcp/libs/mcp_my_client.d.ts.map +1 -1
  13. package/dist/fastbrowser_mcp/libs/mcp_my_client.js +8 -0
  14. package/dist/fastbrowser_mcp/libs/mcp_my_client.js.map +1 -1
  15. package/dist/fastbrowser_mcp/libs/mcp_target_helper.d.ts.map +1 -1
  16. package/dist/fastbrowser_mcp/libs/mcp_target_helper.js +15 -2
  17. package/dist/fastbrowser_mcp/libs/mcp_target_helper.js.map +1 -1
  18. package/dist/fastbrowser_mcp/libs/playwright_a11y_helper.d.ts +1 -0
  19. package/dist/fastbrowser_mcp/libs/playwright_a11y_helper.d.ts.map +1 -1
  20. package/dist/fastbrowser_mcp/libs/playwright_a11y_helper.js +33 -1
  21. package/dist/fastbrowser_mcp/libs/playwright_a11y_helper.js.map +1 -1
  22. package/dist/fastbrowser_mcp/libs/schemas.d.ts +4 -0
  23. package/dist/fastbrowser_mcp/libs/schemas.d.ts.map +1 -1
  24. package/dist/fastbrowser_mcp/libs/schemas.js +6 -0
  25. package/dist/fastbrowser_mcp/libs/schemas.js.map +1 -1
  26. package/dist/shared/logger.d.ts +86 -0
  27. package/dist/shared/logger.d.ts.map +1 -0
  28. package/dist/shared/logger.js +269 -0
  29. package/dist/shared/logger.js.map +1 -0
  30. package/docs/brainstorm_scrap_by_ai.md +1 -1
  31. package/docs/feature_support_cli.md +7 -8
  32. package/docs/target_tools/target_tools_chrome_devtools.md +963 -0
  33. package/docs/target_tools/target_tools_playwright.md +763 -0
  34. package/examples/linkedin_cli/linked_dm.sh +19 -0
  35. package/examples/linkedin_cli/linked_post.sh +13 -0
  36. package/examples/linkedin_cli/linkedin.snapshot.txt +1245 -0
  37. package/examples/todomvc/todomvc.a11y.txt +44 -0
  38. package/examples/todomvc/todomvc.sh +11 -0
  39. package/examples/wttj_cli/fastbrowser_helper.ts +39 -0
  40. package/examples/wttj_cli/wttf_job-original.a11y.txt +652 -0
  41. package/examples/wttj_cli/wttf_job.a11y.txt +317 -0
  42. package/examples/{welcometothejungle/wttj-job.ts → wttj_cli/wttj_job copy.ts } +60 -11
  43. package/examples/wttj_cli/wttj_job.ts +179 -0
  44. package/examples/wttj_cli/wttj_search.ts +162 -0
  45. package/package.json +10 -3
  46. package/skills/fastbrowser/SKILL.md +10 -11
  47. package/skills/fastbrowser-script/SKILL.md +4 -4
  48. package/src/fastbrowser_cli/fastbrowser_cli.ts +14 -25
  49. package/src/fastbrowser_cli/libs/query-builder.ts +6 -0
  50. package/src/fastbrowser_mcp/fastbrowser_mcp.ts +231 -36
  51. package/src/fastbrowser_mcp/libs/mcp_my_client.ts +17 -0
  52. package/src/fastbrowser_mcp/libs/mcp_target_helper.ts +15 -2
  53. package/src/fastbrowser_mcp/libs/playwright_a11y_helper.ts +33 -1
  54. package/src/fastbrowser_mcp/libs/schemas.ts +6 -0
  55. package/src/shared/logger.ts +317 -0
  56. package/test.a11y.txt +828 -0
  57. package/tests/query-builder.test.ts +51 -11
  58. package/examples/welcometothejungle/fastbrowser_helper.ts +0 -39
  59. package/examples/welcometothejungle/wttj-search.ts +0 -82
  60. /package/examples/{post-to-x.sh → twitter_cli/twitter_post.sh} +0 -0
@@ -18,6 +18,9 @@ export const QuerySelectorInputSchema = z.object({
18
18
  withAncestors: z.boolean()
19
19
  .describe("Whether to include ancestor nodes in the result")
20
20
  .default(false),
21
+ withChildren: z.boolean()
22
+ .describe("Whether to include descendant nodes (subtree) of each matched node in the result")
23
+ .default(false),
21
24
  });
22
25
 
23
26
  export const QuerySelectorsInputSchema = z.object({
@@ -31,6 +34,9 @@ export const QuerySelectorFirstInputSchema = z.object({
31
34
  withAncestors: z.boolean()
32
35
  .describe("Whether to include ancestor nodes in the result")
33
36
  .default(false),
37
+ withChildren: z.boolean()
38
+ .describe("Whether to include descendant nodes (subtree) of each matched node in the result")
39
+ .default(false),
34
40
  });
35
41
 
36
42
  export const QuerySelectorsFirstInputSchema = z.object({
@@ -0,0 +1,317 @@
1
+ // npm imports
2
+ import Chalk from 'chalk';
3
+ import { minimatch } from 'minimatch'
4
+
5
+ ///////////////////////////////////////////////////////////////////////////////
6
+ // Logger Type Definitions
7
+ ///////////////////////////////////////////////////////////////////////////////
8
+
9
+ /** Supported log levels in order of severity */
10
+ export type LogLevel = 'DEBUG' | 'INFO' | 'WARN' | 'ERROR';
11
+ type LoggerPattern = { enable: boolean; glob: string; level: LogLevel };
12
+
13
+ ///////////////////////////////////////////////////////////////////////////////
14
+ // Logger Class
15
+ ///////////////////////////////////////////////////////////////////////////////
16
+
17
+ export type LoggerConfig = {
18
+ /**
19
+ * If true, all log levels go to stderr. Otherwise, INFO and DEBUG go to stdout, WARN and ERROR go to stderr.
20
+ * This is useful for environments like Docker where you want all logs to go to the same stream.
21
+ */
22
+ allToStderr: boolean;
23
+ }
24
+
25
+ /**
26
+ * Simple logging utility with severity levels, colored output, and metadata support
27
+ */
28
+ export class Logger {
29
+ static LEVEL = {
30
+ DEBUG: 'DEBUG' as LogLevel,
31
+ INFO: 'INFO' as LogLevel,
32
+ WARN: 'WARN' as LogLevel,
33
+ ERROR: 'ERROR' as LogLevel,
34
+ }
35
+ static DEFAULT_LEVEL: LogLevel = Logger.LEVEL.WARN
36
+ /** Rank mapping for log level filtering */
37
+ static LEVEL_RANK: Record<LogLevel, number> = {
38
+ DEBUG: 3,
39
+ INFO: 2,
40
+ WARN: 1,
41
+ ERROR: 0
42
+ };
43
+ /**
44
+ * Maximum length for filename in log output; longer names will be truncated with ellipsis. This is to prevent log lines
45
+ * from becoming too long due to long file paths.
46
+ */
47
+ private static readonly _maxFilenameLength: number = 30;
48
+
49
+ /** Color scheme for each log level */
50
+ private static readonly LEVEL_COLOR: Record<LogLevel, (s: string) => string> = {
51
+ DEBUG: Chalk.gray,
52
+ INFO: Chalk.cyan,
53
+ WARN: Chalk.yellow,
54
+ ERROR: Chalk.red,
55
+ };
56
+
57
+ //
58
+ private static readonly LoggerConfigDefault: LoggerConfig = {
59
+ allToStderr: false,
60
+ }
61
+ private _loggerConfig: LoggerConfig = Logger.LoggerConfigDefault;
62
+
63
+ ///////////////////////////////////////////////////////////////////////////////
64
+ ///////////////////////////////////////////////////////////////////////////////
65
+ // constructor
66
+ ///////////////////////////////////////////////////////////////////////////////
67
+ ///////////////////////////////////////////////////////////////////////////////
68
+
69
+ private readonly _absFilename: string;
70
+ private readonly _filenameWithoutExt: string;
71
+ private readonly _parsedPatterns: LoggerPattern[];
72
+
73
+ /**
74
+ * Factory method to create a Logger from import.meta.url
75
+ * @param metaUrl - import.meta.url from the calling module
76
+ * @param minLevel - Minimum log level to display (defaults to WARN)
77
+ */
78
+ static fromMetaUrl(metaUrl: string, loggerConfig: LoggerConfig = Logger.LoggerConfigDefault): Logger {
79
+ // Use WHATWG URL (available in Node and browsers) instead of node:url.fileURLToPath
80
+ // so this module stays browser-safe when bundled by Vite.
81
+ const parsedUrl: URL = new URL(metaUrl);
82
+ const absFilename: string = parsedUrl.pathname;
83
+ return new Logger(absFilename, loggerConfig);
84
+ }
85
+
86
+ /**
87
+ * @param absFilename - Logger name (typically module path)
88
+ * @param minLevel - Minimum log level to display (defaults to INFO)
89
+ */
90
+ private constructor(absFilename: string, loggerConfig: LoggerConfig) {
91
+ this._absFilename = absFilename;
92
+ this._loggerConfig = loggerConfig;
93
+
94
+ // Strip file extension from filename for glob matching. Done with string ops
95
+ // so this module avoids node:path and remains browser-safe.
96
+ const lastDotIndex: number = this._absFilename.lastIndexOf('.');
97
+ const lastSeparatorIndex: number = this._absFilename.lastIndexOf('/');
98
+ const hasExtension: boolean = lastDotIndex !== -1 && lastDotIndex > lastSeparatorIndex;
99
+ this._filenameWithoutExt = hasExtension === true
100
+ ? this._absFilename.slice(0, lastDotIndex)
101
+ : this._absFilename;
102
+
103
+ // parse LOGGER environment variable for log filtering patterns. `process` is
104
+ // undefined in browsers, so guard the access.
105
+ const loggerEnvVar: string | undefined = typeof process !== 'undefined'
106
+ ? process.env['LOGGER']
107
+ : undefined;
108
+ if (loggerEnvVar === undefined || loggerEnvVar === '') {
109
+ this._parsedPatterns = [];
110
+ } else {
111
+ this._parsedPatterns = Logger.parseLoggerEnv(loggerEnvVar);
112
+
113
+ }
114
+ }
115
+
116
+ ///////////////////////////////////////////////////////////////////////////////
117
+ ///////////////////////////////////////////////////////////////////////////////
118
+ // log filtering logic based on LOGGER environment variable
119
+ ///////////////////////////////////////////////////////////////////////////////
120
+ ///////////////////////////////////////////////////////////////////////////////
121
+
122
+ /**
123
+ * LOGGER environment variable domain specific language:
124
+ * - comma separated list of patterns
125
+ * - pattern1,pattern2,...,patternN
126
+ * Each pattern is:
127
+ * - {sign}{filename_glob}:{log_level}
128
+ * - sign: [-+] to add/remove filter
129
+ * - if omitted, defaults to + (enable)
130
+ * - if log_level is omitted, it defaults to WARN
131
+ * - log_level can be one of DEBUG, INFO, WARN, ERROR
132
+ * - filename_glob is applied to __filename from import.meta.url without the file extension
133
+ *
134
+ * Example:
135
+ * - LOGGER="*strategy*" enables WARN logs for all loggers with "strategy" in their name
136
+ * - LOGGER="*:DEBUG" enables DEBUG logs for all loggers
137
+ * - LOGGER="*strategy:DEBUG" enables DEBUG logs for all loggers with "strategy" in their name
138
+ * - LOGGER="-*strategy:DEBUG" disables DEBUG logs for all loggers with "strategy" in their name
139
+ * - LOGGER="*trading-core*:INFO" enables INFO logs for all loggers in the trading-core package
140
+ * - LOGGER='**:INFO' npm run dev:batch enables INFO logs for all loggers
141
+ */
142
+ private static parseLoggerEnv(loggerEnv: string): Array<LoggerPattern> {
143
+ const loggerPatterns: Array<LoggerPattern> = [];
144
+ for (const rawPattern of loggerEnv.split(',')) {
145
+ const trimmedPattern = rawPattern.trim();
146
+ if (trimmedPattern.length === 0) continue;
147
+
148
+ let enable = true;
149
+ let patternBody = trimmedPattern;
150
+ if (patternBody.startsWith('-')) {
151
+ enable = false;
152
+ patternBody = patternBody.slice(1);
153
+ } else if (patternBody.startsWith('+')) {
154
+ patternBody = patternBody.slice(1);
155
+ }
156
+
157
+ const colonIndex = patternBody.lastIndexOf(':');
158
+ let glob: string;
159
+ let logLevel: LogLevel = Logger.DEFAULT_LEVEL;
160
+ if (colonIndex !== -1) {
161
+ const levelCandidate = patternBody.slice(colonIndex + 1).toUpperCase();
162
+ if (levelCandidate in Logger.LEVEL_RANK) {
163
+ glob = patternBody.slice(0, colonIndex);
164
+ logLevel = levelCandidate as LogLevel;
165
+ } else {
166
+ glob = patternBody;
167
+ }
168
+ } else {
169
+ glob = patternBody;
170
+ }
171
+ const loggerPattern: LoggerPattern = { enable, glob, level: logLevel };
172
+ loggerPatterns.push(loggerPattern);
173
+ }
174
+ return loggerPatterns;
175
+ }
176
+
177
+ /**
178
+ * Determine if a log message with the given log level should be filtered out based on the LOGGER environment variable patterns
179
+ * and the logger's filename.
180
+ * @param logLevel - Log level of the message
181
+ * @returns boolean indicating whether the message should be filtered out
182
+ */
183
+ private shouldBeFiltered(logLevel: LogLevel): boolean {
184
+ // If no LOGGER env variable is set, default to filtering out logs below INFO
185
+ if (this._parsedPatterns.length === 0) {
186
+ const currentLevelRank = Logger.LEVEL_RANK[logLevel];
187
+ const minLevelRank = Logger.LEVEL_RANK[Logger.DEFAULT_LEVEL];
188
+ const shouldBeFiltered = currentLevelRank > minLevelRank;
189
+ return shouldBeFiltered;
190
+ }
191
+
192
+ // Walk patterns in order; last matching pattern wins
193
+ let minLevel: LogLevel = Logger.DEFAULT_LEVEL;
194
+ let hasMatched = false;
195
+ let isEnabled = true;
196
+ for (const parsedPattern of this._parsedPatterns) {
197
+ const globMatch: boolean = minimatch(this._filenameWithoutExt, parsedPattern.glob);
198
+ if (globMatch === false) continue;
199
+ hasMatched = true;
200
+ isEnabled = parsedPattern.enable;
201
+ minLevel = parsedPattern.level;
202
+ }
203
+
204
+ // if there is filter, and it doesn't match, then filter out
205
+ if (hasMatched === false) return true;
206
+ // if there is filter, and it matches, but it's disabled, then filter out
207
+ if (isEnabled === false) return true;
208
+
209
+ // if there is filter, and it matches, and it's enabled, then check log level
210
+ const currentLevelRank = Logger.LEVEL_RANK[logLevel];
211
+ const minLevelRank = Logger.LEVEL_RANK[minLevel];
212
+ const shouldBeFiltered = currentLevelRank > minLevelRank;
213
+
214
+ // if log level is below the minimum level specified in the filter, then filter out
215
+ return shouldBeFiltered;
216
+ }
217
+
218
+ ///////////////////////////////////////////////////////////////////////////////
219
+ ///////////////////////////////////////////////////////////////////////////////
220
+ // private log function
221
+ ///////////////////////////////////////////////////////////////////////////////
222
+ ///////////////////////////////////////////////////////////////////////////////
223
+
224
+ /**
225
+ * Core logging method
226
+ * @param logLevel - Log level
227
+ * @param msg - Log message
228
+ * @param meta - Optional metadata object to display
229
+ */
230
+ private _log(logLevel: LogLevel, msg: string, meta?: Record<string, unknown>): void {
231
+ // // Skip if log level is below minimum threshold
232
+ const shouldBeFiltered = this.shouldBeFiltered(logLevel);
233
+ if (shouldBeFiltered) return;
234
+
235
+ // Create a nounce base on this._filenameWithoutExt string
236
+ const nounceFromFilename = this._filenameWithoutExt.split('').reduce((acc, char) => acc + char.charCodeAt(0), 0);
237
+ const colorFns = [
238
+ Chalk.magenta,
239
+ Chalk.blueBright,
240
+ Chalk.green,
241
+ Chalk.yellow,
242
+ ];
243
+ const colorFn = colorFns[nounceFromFilename % colorFns.length];
244
+
245
+ const timestamp = new Date()
246
+ const colorize = Logger.LEVEL_COLOR[logLevel];
247
+ const fileName15Char = this._filenameWithoutExt.length <= Logger._maxFilenameLength
248
+ ? this._filenameWithoutExt
249
+ : '...' + this._filenameWithoutExt.slice(-Logger._maxFilenameLength + 3);
250
+ const parts = [
251
+ // Chalk.dim(timestamp.toISOString()),
252
+ colorize(logLevel.padEnd(5)),
253
+ colorFn(fileName15Char.padEnd(15)),
254
+ msg,
255
+ ];
256
+
257
+ // Append metadata if provided
258
+ if (meta !== undefined && Object.keys(meta).length > 0) {
259
+ const metaString = Object.entries(meta)
260
+ .map(([key, value]) => `${Chalk.green(key)}=${Chalk.white(typeof value === 'string' ? value : JSON.stringify(value))}`)
261
+ .join(' ');
262
+ parts.push(Chalk.dim('|'), metaString);
263
+ }
264
+
265
+ const line = parts.join(' ');
266
+ // Error and warn levels go to stderr, others to stdout
267
+ if (logLevel === 'WARN' || logLevel === 'ERROR' || this._loggerConfig.allToStderr === true) {
268
+ console.error(line);
269
+ } else {
270
+ console.log(line);
271
+ }
272
+ }
273
+
274
+ ///////////////////////////////////////////////////////////////////////////////
275
+ ///////////////////////////////////////////////////////////////////////////////
276
+ // public logging methods
277
+ ///////////////////////////////////////////////////////////////////////////////
278
+ ///////////////////////////////////////////////////////////////////////////////
279
+
280
+ // Public logging methods for each severity level
281
+ debug(msg: string, meta?: Record<string, unknown>): void {
282
+ this._log('DEBUG', msg, meta);
283
+ }
284
+ info(msg: string, meta?: Record<string, unknown>): void {
285
+ this._log('INFO', msg, meta);
286
+ }
287
+ warn(msg: string, meta?: Record<string, unknown>): void {
288
+ this._log('WARN', msg, meta);
289
+ }
290
+ error(msg: string, meta?: Record<string, unknown>): void {
291
+ this._log('ERROR', msg, meta);
292
+ }
293
+ }
294
+
295
+ ///////////////////////////////////////////////////////////////////////////////
296
+ ///////////////////////////////////////////////////////////////////////////////
297
+ // Usage Example
298
+ ///////////////////////////////////////////////////////////////////////////////
299
+ ///////////////////////////////////////////////////////////////////////////////
300
+
301
+ async function usageExample() {
302
+ process.env.LOGGER = '**:INFO'; // Enable DEBUG logs for this logger
303
+ console.log(Chalk.blue('--- Logger Usage Example ---'));
304
+ console.log(Chalk.blue('Current LOGGER env variable:'), process.env.LOGGER);
305
+
306
+ const logger = Logger.fromMetaUrl(import.meta.url);
307
+ logger.debug('This is a debug message', { user: 'alice', action: 'testDebug' });
308
+ logger.info('This is an info message', { user: 'bob', action: 'testInfo' });
309
+ logger.warn('This is a warning message', { user: 'carol', action: 'testWarn' });
310
+ logger.error('This is an error message', { user: 'dave', action: 'testError' });
311
+ }
312
+
313
+ if (import.meta.main) {
314
+ usageExample().catch(err => {
315
+ console.error('Error in usage example:', err);
316
+ });
317
+ }