builtwith-official-cli 1.0.0

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.
@@ -0,0 +1,28 @@
1
+ 'use strict';
2
+
3
+ const { requireKey } = require('../config');
4
+ const { request } = require('../client');
5
+ const output = require('../output');
6
+ const ora = require('ora');
7
+
8
+ module.exports = function registerFree(program) {
9
+ const free = program.command('free').description('Free API lookup');
10
+
11
+ free
12
+ .command('lookup <domain>')
13
+ .description('Free category/group counts for a domain')
14
+ .action(async (domainArg) => {
15
+ const opts = program.opts();
16
+ if (opts.noColor) output.setNoColor(true);
17
+ const key = requireKey(opts.key);
18
+ const params = { KEY: key, LOOKUP: domainArg };
19
+ const spinner = opts.quiet ? null : ora({ text: `Fetching free data for ${domainArg}...`, stream: process.stderr }).start();
20
+ try {
21
+ const data = await request('free', params, { dryRun: opts.dryRun, debug: opts.debug, spinner });
22
+ output.print(data, { format: opts.format });
23
+ } catch (err) {
24
+ if (spinner) spinner.stop();
25
+ throw err;
26
+ }
27
+ });
28
+ };
@@ -0,0 +1,28 @@
1
+ 'use strict';
2
+
3
+ const { requireKey } = require('../config');
4
+ const { request } = require('../client');
5
+ const output = require('../output');
6
+ const ora = require('ora');
7
+
8
+ module.exports = function registerKeywords(program) {
9
+ const kw = program.command('keywords').description('Keyword data lookup');
10
+
11
+ kw
12
+ .command('lookup <domain>')
13
+ .description('Get keyword data for a domain')
14
+ .action(async (domainArg) => {
15
+ const opts = program.opts();
16
+ if (opts.noColor) output.setNoColor(true);
17
+ const key = requireKey(opts.key);
18
+ const params = { KEY: key, LOOKUP: domainArg };
19
+ const spinner = opts.quiet ? null : ora({ text: `Fetching keywords for ${domainArg}...`, stream: process.stderr }).start();
20
+ try {
21
+ const data = await request('keywords', params, { dryRun: opts.dryRun, debug: opts.debug, spinner });
22
+ output.print(data, { format: opts.format });
23
+ } catch (err) {
24
+ if (spinner) spinner.stop();
25
+ throw err;
26
+ }
27
+ });
28
+ };
@@ -0,0 +1,30 @@
1
+ 'use strict';
2
+
3
+ const { requireKey } = require('../config');
4
+ const { request } = require('../client');
5
+ const output = require('../output');
6
+ const ora = require('ora');
7
+
8
+ module.exports = function registerLists(program) {
9
+ const lists = program.command('lists').description('Technology lists');
10
+
11
+ lists
12
+ .command('tech <tech>')
13
+ .description('Get list of sites using a technology')
14
+ .option('--offset <n>', 'Result offset', '0')
15
+ .option('--limit <n>', 'Max results', '20')
16
+ .action(async (tech, cmdOpts) => {
17
+ const opts = program.opts();
18
+ if (opts.noColor) output.setNoColor(true);
19
+ const key = requireKey(opts.key);
20
+ const params = { KEY: key, TECH: tech, OFFSET: cmdOpts.offset, LIMIT: cmdOpts.limit };
21
+ const spinner = opts.quiet ? null : ora({ text: `Fetching list for ${tech}...`, stream: process.stderr }).start();
22
+ try {
23
+ const data = await request('lists', params, { dryRun: opts.dryRun, debug: opts.debug, spinner });
24
+ output.print(data, { format: opts.format });
25
+ } catch (err) {
26
+ if (spinner) spinner.stop();
27
+ throw err;
28
+ }
29
+ });
30
+ };
@@ -0,0 +1,61 @@
1
+ 'use strict';
2
+
3
+ const { requireKey } = require('../config');
4
+ const { connect } = require('../ws-client');
5
+ const { EXIT_CODES } = require('../errors');
6
+ const output = require('../output');
7
+
8
+ module.exports = function registerLive(program) {
9
+ const live = program.command('live').description('Live technology feed');
10
+
11
+ live
12
+ .command('feed')
13
+ .description('Stream live technology detection events as NDJSON')
14
+ .option('--duration <seconds>', 'Auto-stop after N seconds')
15
+ .action(async (cmdOpts) => {
16
+ const opts = program.opts();
17
+ if (opts.noColor) output.setNoColor(true);
18
+ const key = requireKey(opts.key);
19
+
20
+ let ws;
21
+ let timer;
22
+
23
+ function cleanup(exitCode) {
24
+ if (timer) clearTimeout(timer);
25
+ if (ws) ws.close();
26
+ process.exit(exitCode);
27
+ }
28
+
29
+ // SIGINT → exit code 8 (INTERRUPTED)
30
+ process.on('SIGINT', () => {
31
+ if (!opts.quiet) output.info('Interrupted');
32
+ cleanup(EXIT_CODES.INTERRUPTED);
33
+ });
34
+
35
+ ws = connect(key, {
36
+ debug: opts.debug,
37
+ onMessage(event) {
38
+ // NDJSON: one JSON object per line to stdout
39
+ process.stdout.write(JSON.stringify(event) + '\n');
40
+ },
41
+ onError(err) {
42
+ output.error(err.message);
43
+ cleanup(err.exitCode || EXIT_CODES.NETWORK);
44
+ },
45
+ onClose(code) {
46
+ if (!opts.quiet) output.info(`WebSocket closed (code: ${code})`);
47
+ process.exit(EXIT_CODES.SUCCESS);
48
+ },
49
+ });
50
+
51
+ if (cmdOpts.duration) {
52
+ const secs = parseInt(cmdOpts.duration, 10);
53
+ if (!isNaN(secs) && secs > 0) {
54
+ timer = setTimeout(() => {
55
+ if (!opts.quiet) output.info(`Duration ${secs}s elapsed, closing`);
56
+ cleanup(EXIT_CODES.SUCCESS);
57
+ }, secs * 1000);
58
+ }
59
+ }
60
+ });
61
+ };
@@ -0,0 +1,333 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * MCP (Model Context Protocol) stdio server.
5
+ * Implements JSON-RPC 2.0 over stdin/stdout — no SDK dependency.
6
+ *
7
+ * stdout = JSON-RPC channel (never write anything else here)
8
+ * stderr = debug/error logging only
9
+ */
10
+
11
+ const { requireKey } = require('../config');
12
+ const { request } = require('../client');
13
+ const { EXIT_CODES } = require('../errors');
14
+
15
+ // ─── Tool definitions ────────────────────────────────────────────────────────
16
+
17
+ const TOOLS = [
18
+ {
19
+ name: 'domain_lookup',
20
+ description: 'Look up the technology stack and metadata for a domain using the BuiltWith Domain API.',
21
+ inputSchema: {
22
+ type: 'object',
23
+ properties: {
24
+ domain: { type: 'string', description: 'Domain to look up (e.g. "shopify.com")' },
25
+ nopii: { type: 'boolean', description: 'Exclude PII data' },
26
+ nometa: { type: 'boolean', description: 'Exclude meta data' },
27
+ noattr: { type: 'boolean', description: 'Exclude attribution data' },
28
+ liveonly: { type: 'boolean', description: 'Only return currently-live technologies' },
29
+ fdrange: { type: 'string', description: 'First-detected range (YYYYMMDD-YYYYMMDD)' },
30
+ ldrange: { type: 'string', description: 'Last-detected range (YYYYMMDD-YYYYMMDD)' },
31
+ },
32
+ required: ['domain'],
33
+ },
34
+ },
35
+ {
36
+ name: 'lists_tech',
37
+ description: 'Get a list of domains currently using a specific technology.',
38
+ inputSchema: {
39
+ type: 'object',
40
+ properties: {
41
+ tech: { type: 'string', description: 'Technology name (e.g. "WordPress", "Shopify")' },
42
+ offset: { type: 'number', description: 'Result offset for pagination (default 0)' },
43
+ limit: { type: 'number', description: 'Number of results (default 20)' },
44
+ },
45
+ required: ['tech'],
46
+ },
47
+ },
48
+ {
49
+ name: 'relationships_lookup',
50
+ description: 'Find domains related to a given domain (shared infrastructure, ownership, etc.).',
51
+ inputSchema: {
52
+ type: 'object',
53
+ properties: {
54
+ domain: { type: 'string', description: 'Domain to look up relationships for' },
55
+ },
56
+ required: ['domain'],
57
+ },
58
+ },
59
+ {
60
+ name: 'free_lookup',
61
+ description: 'Free-tier category and group technology counts for a domain.',
62
+ inputSchema: {
63
+ type: 'object',
64
+ properties: {
65
+ domain: { type: 'string', description: 'Domain to look up' },
66
+ },
67
+ required: ['domain'],
68
+ },
69
+ },
70
+ {
71
+ name: 'company_find',
72
+ description: 'Find domains and web properties associated with a company name.',
73
+ inputSchema: {
74
+ type: 'object',
75
+ properties: {
76
+ name: { type: 'string', description: 'Company name to search for' },
77
+ },
78
+ required: ['name'],
79
+ },
80
+ },
81
+ {
82
+ name: 'tags_lookup',
83
+ description: 'Find domains related to an IP address or other tag attributes.',
84
+ inputSchema: {
85
+ type: 'object',
86
+ properties: {
87
+ lookup: { type: 'string', description: 'IP address or tag value to look up' },
88
+ },
89
+ required: ['lookup'],
90
+ },
91
+ },
92
+ {
93
+ name: 'recommendations_lookup',
94
+ description: 'Get technology recommendations for a domain based on its current stack.',
95
+ inputSchema: {
96
+ type: 'object',
97
+ properties: {
98
+ domain: { type: 'string', description: 'Domain to get recommendations for' },
99
+ },
100
+ required: ['domain'],
101
+ },
102
+ },
103
+ {
104
+ name: 'redirects_lookup',
105
+ description: 'Get live and historical redirect chains for a domain.',
106
+ inputSchema: {
107
+ type: 'object',
108
+ properties: {
109
+ domain: { type: 'string', description: 'Domain to look up redirects for' },
110
+ },
111
+ required: ['domain'],
112
+ },
113
+ },
114
+ {
115
+ name: 'keywords_lookup',
116
+ description: 'Get keyword data associated with a domain.',
117
+ inputSchema: {
118
+ type: 'object',
119
+ properties: {
120
+ domain: { type: 'string', description: 'Domain to look up keywords for' },
121
+ },
122
+ required: ['domain'],
123
+ },
124
+ },
125
+ {
126
+ name: 'trends_tech',
127
+ description: 'Get historical trend data showing adoption of a technology over time.',
128
+ inputSchema: {
129
+ type: 'object',
130
+ properties: {
131
+ tech: { type: 'string', description: 'Technology name (e.g. "React", "WordPress")' },
132
+ },
133
+ required: ['tech'],
134
+ },
135
+ },
136
+ {
137
+ name: 'products_search',
138
+ description: 'Search for ecommerce products across BuiltWith-indexed stores.',
139
+ inputSchema: {
140
+ type: 'object',
141
+ properties: {
142
+ query: { type: 'string', description: 'Product search query' },
143
+ page: { type: 'number', description: 'Page number (default 1)' },
144
+ limit: { type: 'number', description: 'Results per page (default 20)' },
145
+ },
146
+ required: ['query'],
147
+ },
148
+ },
149
+ {
150
+ name: 'trust_lookup',
151
+ description: 'Get a trust/quality score for a domain.',
152
+ inputSchema: {
153
+ type: 'object',
154
+ properties: {
155
+ domain: { type: 'string', description: 'Domain to score' },
156
+ },
157
+ required: ['domain'],
158
+ },
159
+ },
160
+ {
161
+ name: 'account_whoami',
162
+ description: 'Return the identity and details of the authenticated BuiltWith account.',
163
+ inputSchema: {
164
+ type: 'object',
165
+ properties: {},
166
+ },
167
+ },
168
+ {
169
+ name: 'account_usage',
170
+ description: 'Return API usage statistics for the authenticated BuiltWith account.',
171
+ inputSchema: {
172
+ type: 'object',
173
+ properties: {},
174
+ },
175
+ },
176
+ ];
177
+
178
+ // ─── Tool call handler ───────────────────────────────────────────────────────
179
+
180
+ async function callTool(name, args, key, debug) {
181
+ const opts = { debug };
182
+
183
+ switch (name) {
184
+ case 'domain_lookup': {
185
+ const params = { KEY: key, LOOKUP: args.domain };
186
+ if (args.nopii) params.NOPII = true;
187
+ if (args.nometa) params.NOMETA = true;
188
+ if (args.noattr) params.NOATTR = true;
189
+ if (args.liveonly) params.LIVEONLY = true;
190
+ if (args.fdrange) params.FDRANGE = args.fdrange;
191
+ if (args.ldrange) params.LDRANGE = args.ldrange;
192
+ return request('domain', params, opts);
193
+ }
194
+ case 'lists_tech':
195
+ return request('lists', { KEY: key, TECH: args.tech, OFFSET: args.offset || 0, LIMIT: args.limit || 20 }, opts);
196
+ case 'relationships_lookup':
197
+ return request('relationships', { KEY: key, LOOKUP: args.domain }, opts);
198
+ case 'free_lookup':
199
+ return request('free', { KEY: key, LOOKUP: args.domain }, opts);
200
+ case 'company_find':
201
+ return request('company', { KEY: key, COMPANY: args.name }, opts);
202
+ case 'tags_lookup':
203
+ return request('tags', { KEY: key, LOOKUP: args.lookup }, opts);
204
+ case 'recommendations_lookup':
205
+ return request('recommendations', { KEY: key, LOOKUP: args.domain }, opts);
206
+ case 'redirects_lookup':
207
+ return request('redirects', { KEY: key, LOOKUP: args.domain }, opts);
208
+ case 'keywords_lookup':
209
+ return request('keywords', { KEY: key, LOOKUP: args.domain }, opts);
210
+ case 'trends_tech':
211
+ return request('trends', { KEY: key, TECH: args.tech }, opts);
212
+ case 'products_search':
213
+ return request('products', { KEY: key, QUERY: args.query, PAGE: args.page || 1, LIMIT: args.limit || 20 }, opts);
214
+ case 'trust_lookup':
215
+ return request('trust', { KEY: key, LOOKUP: args.domain }, opts);
216
+ case 'account_whoami':
217
+ return request('whoami', { KEY: key }, opts);
218
+ case 'account_usage':
219
+ return request('usage', { KEY: key }, opts);
220
+ default:
221
+ throw new Error(`Unknown tool: ${name}`);
222
+ }
223
+ }
224
+
225
+ // ─── JSON-RPC helpers ────────────────────────────────────────────────────────
226
+
227
+ function send(obj) {
228
+ process.stdout.write(JSON.stringify(obj) + '\n');
229
+ }
230
+
231
+ function respond(id, result) {
232
+ send({ jsonrpc: '2.0', id, result });
233
+ }
234
+
235
+ function respondError(id, code, message) {
236
+ send({ jsonrpc: '2.0', id, error: { code, message } });
237
+ }
238
+
239
+ // ─── Commander registration ──────────────────────────────────────────────────
240
+
241
+ module.exports = function registerMcp(program) {
242
+ program
243
+ .command('mcp')
244
+ .description('Start an MCP stdio server exposing BuiltWith API as tools')
245
+ .action(async () => {
246
+ const opts = program.opts();
247
+ const debug = opts.debug || false;
248
+
249
+ // Resolve API key upfront — fail fast before stdio server starts
250
+ let key;
251
+ try {
252
+ key = requireKey(opts.key);
253
+ } catch (err) {
254
+ process.stderr.write(`[builtwith-mcp] ${err.message}\n`);
255
+ process.exit(EXIT_CODES.AUTH);
256
+ }
257
+
258
+ if (debug) process.stderr.write('[builtwith-mcp] Server starting on stdio\n');
259
+
260
+ // Read stdin line-by-line (JSON-RPC 2.0)
261
+ let buffer = '';
262
+ process.stdin.setEncoding('utf8');
263
+ process.stdin.resume();
264
+
265
+ process.stdin.on('data', (chunk) => {
266
+ buffer += chunk;
267
+ const lines = buffer.split('\n');
268
+ buffer = lines.pop(); // keep incomplete line
269
+ for (const line of lines) {
270
+ const trimmed = line.trim();
271
+ if (!trimmed) continue;
272
+ handleLine(trimmed, key, debug);
273
+ }
274
+ });
275
+
276
+ process.stdin.on('end', () => {
277
+ process.exit(EXIT_CODES.SUCCESS);
278
+ });
279
+ });
280
+ };
281
+
282
+ async function handleLine(line, key, debug) {
283
+ let msg;
284
+ try {
285
+ msg = JSON.parse(line);
286
+ } catch (_) {
287
+ respondError(null, -32700, 'Parse error');
288
+ return;
289
+ }
290
+
291
+ if (debug) process.stderr.write(`[builtwith-mcp] recv: ${line}\n`);
292
+
293
+ const { id, method, params } = msg;
294
+
295
+ // Notifications (no id) — acknowledge and ignore
296
+ if (id === undefined || id === null) {
297
+ return;
298
+ }
299
+
300
+ switch (method) {
301
+ case 'initialize':
302
+ respond(id, {
303
+ protocolVersion: '2024-11-05',
304
+ serverInfo: { name: 'builtwith', version: '1.0.0' },
305
+ capabilities: { tools: {} },
306
+ });
307
+ break;
308
+
309
+ case 'tools/list':
310
+ respond(id, { tools: TOOLS });
311
+ break;
312
+
313
+ case 'tools/call': {
314
+ const toolName = params && params.name;
315
+ const toolArgs = (params && params.arguments) || {};
316
+ try {
317
+ const data = await callTool(toolName, toolArgs, key, debug);
318
+ respond(id, {
319
+ content: [{ type: 'text', text: JSON.stringify(data) }],
320
+ });
321
+ } catch (err) {
322
+ respond(id, {
323
+ isError: true,
324
+ content: [{ type: 'text', text: err.message }],
325
+ });
326
+ }
327
+ break;
328
+ }
329
+
330
+ default:
331
+ respondError(id, -32601, `Method not found: ${method}`);
332
+ }
333
+ }
@@ -0,0 +1,30 @@
1
+ 'use strict';
2
+
3
+ const { requireKey } = require('../config');
4
+ const { request } = require('../client');
5
+ const output = require('../output');
6
+ const ora = require('ora');
7
+
8
+ module.exports = function registerProducts(program) {
9
+ const products = program.command('products').description('Ecommerce product search');
10
+
11
+ products
12
+ .command('search <query>')
13
+ .description('Search for ecommerce products')
14
+ .option('--page <n>', 'Page number', '1')
15
+ .option('--limit <n>', 'Results per page', '20')
16
+ .action(async (query, cmdOpts) => {
17
+ const opts = program.opts();
18
+ if (opts.noColor) output.setNoColor(true);
19
+ const key = requireKey(opts.key);
20
+ const params = { KEY: key, QUERY: query, PAGE: cmdOpts.page, LIMIT: cmdOpts.limit };
21
+ const spinner = opts.quiet ? null : ora({ text: `Searching products for "${query}"...`, stream: process.stderr }).start();
22
+ try {
23
+ const data = await request('products', params, { dryRun: opts.dryRun, debug: opts.debug, spinner });
24
+ output.print(data, { format: opts.format });
25
+ } catch (err) {
26
+ if (spinner) spinner.stop();
27
+ throw err;
28
+ }
29
+ });
30
+ };
@@ -0,0 +1,28 @@
1
+ 'use strict';
2
+
3
+ const { requireKey } = require('../config');
4
+ const { request } = require('../client');
5
+ const output = require('../output');
6
+ const ora = require('ora');
7
+
8
+ module.exports = function registerRecommendations(program) {
9
+ const rec = program.command('recommendations').description('Technology recommendations');
10
+
11
+ rec
12
+ .command('lookup <domain>')
13
+ .description('Get technology recommendations for a domain')
14
+ .action(async (domainArg) => {
15
+ const opts = program.opts();
16
+ if (opts.noColor) output.setNoColor(true);
17
+ const key = requireKey(opts.key);
18
+ const params = { KEY: key, LOOKUP: domainArg };
19
+ const spinner = opts.quiet ? null : ora({ text: `Fetching recommendations for ${domainArg}...`, stream: process.stderr }).start();
20
+ try {
21
+ const data = await request('recommendations', params, { dryRun: opts.dryRun, debug: opts.debug, spinner });
22
+ output.print(data, { format: opts.format });
23
+ } catch (err) {
24
+ if (spinner) spinner.stop();
25
+ throw err;
26
+ }
27
+ });
28
+ };
@@ -0,0 +1,28 @@
1
+ 'use strict';
2
+
3
+ const { requireKey } = require('../config');
4
+ const { request } = require('../client');
5
+ const output = require('../output');
6
+ const ora = require('ora');
7
+
8
+ module.exports = function registerRedirects(program) {
9
+ const redirects = program.command('redirects').description('Redirect history lookup');
10
+
11
+ redirects
12
+ .command('lookup <domain>')
13
+ .description('Get live and historical redirects for a domain')
14
+ .action(async (domainArg) => {
15
+ const opts = program.opts();
16
+ if (opts.noColor) output.setNoColor(true);
17
+ const key = requireKey(opts.key);
18
+ const params = { KEY: key, LOOKUP: domainArg };
19
+ const spinner = opts.quiet ? null : ora({ text: `Fetching redirects for ${domainArg}...`, stream: process.stderr }).start();
20
+ try {
21
+ const data = await request('redirects', params, { dryRun: opts.dryRun, debug: opts.debug, spinner });
22
+ output.print(data, { format: opts.format });
23
+ } catch (err) {
24
+ if (spinner) spinner.stop();
25
+ throw err;
26
+ }
27
+ });
28
+ };
@@ -0,0 +1,28 @@
1
+ 'use strict';
2
+
3
+ const { requireKey } = require('../config');
4
+ const { request } = require('../client');
5
+ const output = require('../output');
6
+ const ora = require('ora');
7
+
8
+ module.exports = function registerRelationships(program) {
9
+ const rel = program.command('relationships').description('Domain relationship data');
10
+
11
+ rel
12
+ .command('lookup <domain>')
13
+ .description('Look up domains related to a given domain')
14
+ .action(async (domainArg) => {
15
+ const opts = program.opts();
16
+ if (opts.noColor) output.setNoColor(true);
17
+ const key = requireKey(opts.key);
18
+ const params = { KEY: key, LOOKUP: domainArg };
19
+ const spinner = opts.quiet ? null : ora({ text: `Fetching relationships for ${domainArg}...`, stream: process.stderr }).start();
20
+ try {
21
+ const data = await request('relationships', params, { dryRun: opts.dryRun, debug: opts.debug, spinner });
22
+ output.print(data, { format: opts.format });
23
+ } catch (err) {
24
+ if (spinner) spinner.stop();
25
+ throw err;
26
+ }
27
+ });
28
+ };
@@ -0,0 +1,28 @@
1
+ 'use strict';
2
+
3
+ const { requireKey } = require('../config');
4
+ const { request } = require('../client');
5
+ const output = require('../output');
6
+ const ora = require('ora');
7
+
8
+ module.exports = function registerTags(program) {
9
+ const tags = program.command('tags').description('Tags API lookup');
10
+
11
+ tags
12
+ .command('lookup <lookup>')
13
+ .description('Find domains related to IP or other attributes')
14
+ .action(async (lookup) => {
15
+ const opts = program.opts();
16
+ if (opts.noColor) output.setNoColor(true);
17
+ const key = requireKey(opts.key);
18
+ const params = { KEY: key, LOOKUP: lookup };
19
+ const spinner = opts.quiet ? null : ora({ text: `Fetching tags for ${lookup}...`, stream: process.stderr }).start();
20
+ try {
21
+ const data = await request('tags', params, { dryRun: opts.dryRun, debug: opts.debug, spinner });
22
+ output.print(data, { format: opts.format });
23
+ } catch (err) {
24
+ if (spinner) spinner.stop();
25
+ throw err;
26
+ }
27
+ });
28
+ };
@@ -0,0 +1,28 @@
1
+ 'use strict';
2
+
3
+ const { requireKey } = require('../config');
4
+ const { request } = require('../client');
5
+ const output = require('../output');
6
+ const ora = require('ora');
7
+
8
+ module.exports = function registerTrends(program) {
9
+ const trends = program.command('trends').description('Technology trend data');
10
+
11
+ trends
12
+ .command('tech <tech>')
13
+ .description('Get trend data for a technology')
14
+ .action(async (tech) => {
15
+ const opts = program.opts();
16
+ if (opts.noColor) output.setNoColor(true);
17
+ const key = requireKey(opts.key);
18
+ const params = { KEY: key, TECH: tech };
19
+ const spinner = opts.quiet ? null : ora({ text: `Fetching trends for ${tech}...`, stream: process.stderr }).start();
20
+ try {
21
+ const data = await request('trends', params, { dryRun: opts.dryRun, debug: opts.debug, spinner });
22
+ output.print(data, { format: opts.format });
23
+ } catch (err) {
24
+ if (spinner) spinner.stop();
25
+ throw err;
26
+ }
27
+ });
28
+ };
@@ -0,0 +1,28 @@
1
+ 'use strict';
2
+
3
+ const { requireKey } = require('../config');
4
+ const { request } = require('../client');
5
+ const output = require('../output');
6
+ const ora = require('ora');
7
+
8
+ module.exports = function registerTrust(program) {
9
+ const trust = program.command('trust').description('Trust score lookup');
10
+
11
+ trust
12
+ .command('lookup <domain>')
13
+ .description('Get trust score for a domain')
14
+ .action(async (domainArg) => {
15
+ const opts = program.opts();
16
+ if (opts.noColor) output.setNoColor(true);
17
+ const key = requireKey(opts.key);
18
+ const params = { KEY: key, LOOKUP: domainArg };
19
+ const spinner = opts.quiet ? null : ora({ text: `Fetching trust score for ${domainArg}...`, stream: process.stderr }).start();
20
+ try {
21
+ const data = await request('trust', params, { dryRun: opts.dryRun, debug: opts.debug, spinner });
22
+ output.print(data, { format: opts.format });
23
+ } catch (err) {
24
+ if (spinner) spinner.stop();
25
+ throw err;
26
+ }
27
+ });
28
+ };