@mintlify/cli 4.0.1083 → 4.0.1085

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,97 @@
1
+ import chalk from 'chalk';
2
+
3
+ export type OutputFormat = 'table' | 'plain' | 'json' | 'graph';
4
+
5
+ export function resolveFormat(argv: { format?: string; agent?: boolean }): OutputFormat {
6
+ if (argv.agent || process.env.CLAUDECODE === '1') return 'json';
7
+ if (
8
+ argv.format === 'table' ||
9
+ argv.format === 'plain' ||
10
+ argv.format === 'json' ||
11
+ argv.format === 'graph'
12
+ )
13
+ return argv.format;
14
+ return 'plain';
15
+ }
16
+
17
+ export function formatPlainTable(headers: string[], rows: string[][]): string {
18
+ if (rows.length === 0) return '';
19
+
20
+ const colWidths = headers.map((h, i) =>
21
+ Math.max(h.length, ...rows.map((r) => (r[i] ?? '').length))
22
+ );
23
+
24
+ const headerLine = headers.map((h, i) => h.toUpperCase().padEnd(colWidths[i]!)).join('\t');
25
+ const bodyLines = rows.map((row) => row.map((cell, i) => cell.padEnd(colWidths[i]!)).join('\t'));
26
+
27
+ return [headerLine, ...bodyLines].join('\n');
28
+ }
29
+
30
+ export function formatPrettyTable(headers: string[], rows: string[][]): string {
31
+ if (rows.length === 0) return chalk.dim(' No data found.');
32
+
33
+ const colWidths = headers.map((h, i) =>
34
+ Math.max(h.length, ...rows.map((r) => (r[i] ?? '').length))
35
+ );
36
+
37
+ const headerLine = headers.map((h, i) => chalk.bold(h.padEnd(colWidths[i]!))).join(' ');
38
+ const separator = chalk.dim(colWidths.map((w) => '\u2500'.repeat(w)).join('\u2500\u2500'));
39
+ const bodyLines = rows.map((row) => row.map((cell, i) => cell.padEnd(colWidths[i]!)).join(' '));
40
+
41
+ return [headerLine, separator, ...bodyLines].join('\n');
42
+ }
43
+
44
+ function gradientBar(len: number, rgb: [number, number, number]): string {
45
+ let result = '';
46
+ for (let i = 0; i < len; i++) {
47
+ const t = len === 1 ? 1 : i / (len - 1);
48
+ const dim = 0.3 + t * 0.7;
49
+ const r = Math.round(rgb[0] * dim);
50
+ const g = Math.round(rgb[1] * dim);
51
+ const b = Math.round(rgb[2] * dim);
52
+ result += chalk.rgb(r, g, b)('\u2588');
53
+ }
54
+ return result;
55
+ }
56
+
57
+ const COLOR_MAP: Record<string, [number, number, number]> = {
58
+ cyan: [0, 255, 255],
59
+ magenta: [255, 0, 255],
60
+ yellow: [255, 255, 0],
61
+ green: [0, 255, 100],
62
+ blue: [80, 140, 255],
63
+ red: [255, 80, 80],
64
+ };
65
+
66
+ export function formatBarChart(
67
+ items: { label: string; value: number; color?: string }[],
68
+ opts: { maxWidth?: number } = {}
69
+ ): string {
70
+ if (items.length === 0) return chalk.dim(' No data found.');
71
+
72
+ const maxWidth = opts.maxWidth ?? 40;
73
+ const maxVal = Math.max(...items.map((i) => i.value), 1);
74
+ const maxLabel = Math.max(...items.map((i) => i.label.length));
75
+ const maxValStr = Math.max(...items.map((i) => i.value.toLocaleString('en-US').length));
76
+
77
+ return items
78
+ .map((item) => {
79
+ const barLen = Math.round((item.value / maxVal) * maxWidth);
80
+ const rgb = COLOR_MAP[item.color ?? 'cyan'] ?? COLOR_MAP.cyan!;
81
+ const bar = barLen > 0 ? gradientBar(barLen, rgb) : '';
82
+ const pad = ' '.repeat(maxWidth - barLen);
83
+ return ` ${item.label.padEnd(maxLabel)} ${bar}${pad} ${item.value.toLocaleString('en-US').padStart(maxValStr)}`;
84
+ })
85
+ .join('\n');
86
+ }
87
+
88
+ export function formatOutput(
89
+ format: OutputFormat,
90
+ headers: string[],
91
+ rows: string[][],
92
+ jsonData: unknown
93
+ ): string {
94
+ if (format === 'json') return JSON.stringify(jsonData, null, 2);
95
+ if (format === 'plain') return formatPlainTable(headers, rows);
96
+ return formatPrettyTable(headers, rows);
97
+ }
@@ -0,0 +1,132 @@
1
+ export interface KpiResponse {
2
+ humanVisitors: number;
3
+ humanViews: number;
4
+ humanAssistant: number;
5
+ humanSearches: number;
6
+ humanFeedback: number;
7
+ agentVisitors: number;
8
+ agentViews: number;
9
+ agentMcpSearches: number;
10
+ }
11
+
12
+ export interface BucketSummary {
13
+ id: string;
14
+ questionSummary: string;
15
+ size: number;
16
+ status: string;
17
+ lastAsked: string | null;
18
+ lastOccurredAt: string | null;
19
+ createdAt: string;
20
+ }
21
+
22
+ export interface BucketsResponse {
23
+ data: BucketSummary[];
24
+ pagination: { total: number };
25
+ }
26
+
27
+ export interface BucketThread {
28
+ id: string;
29
+ firstUserMessage: string | null;
30
+ feedback: { up: number; down: number };
31
+ resolutionStatus: string | null;
32
+ length: number;
33
+ createdAt: string;
34
+ lastMessageAt: string | null;
35
+ }
36
+
37
+ export interface BucketThreadsResponse {
38
+ data: BucketThread[];
39
+ pagination: { total: number; hasMore: boolean; nextCursor: string | null };
40
+ }
41
+
42
+ export interface FeedbackItem {
43
+ id: string;
44
+ path: string;
45
+ comment: string | null;
46
+ createdAt: string | null;
47
+ source: string;
48
+ status: string;
49
+ helpful?: boolean;
50
+ contact?: string;
51
+ code?: string;
52
+ filename?: string;
53
+ lang?: string;
54
+ }
55
+
56
+ export interface FeedbackResponse {
57
+ feedback: FeedbackItem[];
58
+ nextCursor: string | null;
59
+ hasMore: boolean;
60
+ }
61
+
62
+ export interface FeedbackByPageItem {
63
+ path: string;
64
+ thumbsUp: number;
65
+ thumbsDown: number;
66
+ code: number;
67
+ total: number;
68
+ }
69
+
70
+ export interface FeedbackByPageResponse {
71
+ feedback: FeedbackByPageItem[];
72
+ hasMore: boolean;
73
+ }
74
+
75
+ export interface ConversationSource {
76
+ title: string;
77
+ url: string;
78
+ }
79
+
80
+ export interface Conversation {
81
+ id: string;
82
+ timestamp: string;
83
+ query: string;
84
+ response: string;
85
+ sources: ConversationSource[];
86
+ queryCategory: string | null;
87
+ }
88
+
89
+ export interface ConversationResponse {
90
+ conversations: Conversation[];
91
+ nextCursor: string | null;
92
+ hasMore: boolean;
93
+ }
94
+
95
+ export interface SearchRow {
96
+ searchQuery: string;
97
+ hits: number;
98
+ ctr: number;
99
+ topClickedPage: string | null;
100
+ lastSearchedAt: string;
101
+ }
102
+
103
+ export interface SearchResponse {
104
+ searches: SearchRow[];
105
+ totalSearches: number;
106
+ nextCursor: string | null;
107
+ }
108
+
109
+ export interface TrafficTotals {
110
+ human: number;
111
+ ai: number;
112
+ total: number;
113
+ }
114
+
115
+ export interface TrafficRow {
116
+ path: string;
117
+ human: number;
118
+ ai: number;
119
+ total: number;
120
+ }
121
+
122
+ export interface ViewsResponse {
123
+ totals: TrafficTotals;
124
+ views: TrafficRow[];
125
+ hasMore: boolean;
126
+ }
127
+
128
+ export interface VisitorsResponse {
129
+ totals: TrafficTotals;
130
+ visitors: TrafficRow[];
131
+ hasMore: boolean;
132
+ }
package/src/cli.tsx CHANGED
@@ -19,7 +19,9 @@ import yargs from 'yargs';
19
19
  import { hideBin } from 'yargs/helpers';
20
20
 
21
21
  import { accessibilityCheck } from './accessibilityCheck.js';
22
+ import { analyticsBuilder } from './analytics/index.js';
22
23
  import { setTelemetryEnabled } from './config.js';
24
+ import { getConfigValue, setConfigValue, clearConfigValue } from './config.js';
23
25
  import { CMD_EXEC_PATH } from './constants.js';
24
26
  import {
25
27
  checkPort,
@@ -340,6 +342,91 @@ export const cli = ({ packageName = 'mint' }: { packageName?: string }) => {
340
342
  await terminate(0);
341
343
  }
342
344
  )
345
+ .command('config', 'manage CLI configuration', (yargs) =>
346
+ yargs
347
+ .command(
348
+ 'set <key> <value>',
349
+ 'set a configuration value',
350
+ (yargs) =>
351
+ yargs
352
+ .positional('key', {
353
+ type: 'string',
354
+ demandOption: true,
355
+ description: 'Config key (e.g. subdomain)',
356
+ })
357
+ .positional('value', {
358
+ type: 'string',
359
+ demandOption: true,
360
+ description: 'Config value',
361
+ }),
362
+ async (argv) => {
363
+ const validKeys = ['subdomain', 'dateFrom', 'dateTo'];
364
+ if (!validKeys.includes(argv.key)) {
365
+ addLog(
366
+ <ErrorLog
367
+ message={`Unknown config key: "${argv.key}". Valid keys: ${validKeys.join(', ')}`}
368
+ />
369
+ );
370
+ await terminate(1);
371
+ return;
372
+ }
373
+ await setConfigValue(argv.key, argv.value);
374
+ addLog(<SuccessLog message={`${argv.key} = "${argv.value}"`} />);
375
+ await terminate(0);
376
+ }
377
+ )
378
+ .command(
379
+ 'get <key>',
380
+ 'get a configuration value',
381
+ (yargs) =>
382
+ yargs.positional('key', {
383
+ type: 'string',
384
+ demandOption: true,
385
+ description: 'Config key (e.g. subdomain, dateFrom, dateTo)',
386
+ }),
387
+ async (argv) => {
388
+ const validKeys = ['subdomain', 'dateFrom', 'dateTo'];
389
+ if (!validKeys.includes(argv.key)) {
390
+ addLog(
391
+ <ErrorLog
392
+ message={`Unknown config key: "${argv.key}". Valid keys: ${validKeys.join(', ')}`}
393
+ />
394
+ );
395
+ await terminate(1);
396
+ return;
397
+ }
398
+ const val = getConfigValue(argv.key);
399
+ addLog(<Text>{val ?? 'not set'}</Text>);
400
+ await terminate(0);
401
+ }
402
+ )
403
+ .command(
404
+ 'clear <key>',
405
+ 'remove a configuration value',
406
+ (yargs) =>
407
+ yargs.positional('key', {
408
+ type: 'string',
409
+ demandOption: true,
410
+ description: 'Config key (e.g. subdomain, dateFrom, dateTo)',
411
+ }),
412
+ async (argv) => {
413
+ const validKeys = ['subdomain', 'dateFrom', 'dateTo'];
414
+ if (!validKeys.includes(argv.key)) {
415
+ addLog(
416
+ <ErrorLog
417
+ message={`Unknown config key: "${argv.key}". Valid keys: ${validKeys.join(', ')}`}
418
+ />
419
+ );
420
+ await terminate(1);
421
+ return;
422
+ }
423
+ await clearConfigValue(argv.key);
424
+ addLog(<SuccessLog message={`${argv.key} cleared`} />);
425
+ await terminate(0);
426
+ }
427
+ )
428
+ .demandCommand(1, 'specify a subcommand: set, get, or clear')
429
+ )
343
430
  .command(
344
431
  'update',
345
432
  'update the CLI to the latest version',
@@ -463,6 +550,7 @@ export const cli = ({ packageName = 'mint' }: { packageName?: string }) => {
463
550
  }
464
551
  }
465
552
  )
553
+ .command('analytics', 'view analytics for your documentation', analyticsBuilder)
466
554
  // Print the help menu when the user enters an invalid command.
467
555
  .strictCommands()
468
556
  .demandCommand(1, 'unknown command. see above for the list of supported commands.')
package/src/config.ts CHANGED
@@ -5,6 +5,9 @@ import { CLI_CONFIG_FILE, CONFIG_DIR } from './constants.js';
5
5
 
6
6
  interface MintlifyConfig {
7
7
  telemetryEnabled?: boolean;
8
+ subdomain?: string;
9
+ dateFrom?: string;
10
+ dateTo?: string;
8
11
  }
9
12
 
10
13
  function readConfig(): MintlifyConfig {
@@ -32,3 +35,16 @@ export function isTelemetryEnabled(): boolean {
32
35
  export async function setTelemetryEnabled(enabled: boolean): Promise<void> {
33
36
  await writeConfig({ telemetryEnabled: enabled });
34
37
  }
38
+
39
+ export function getConfigValue(key: string): string | undefined {
40
+ const config = readConfig();
41
+ return config[key as keyof MintlifyConfig] as string | undefined;
42
+ }
43
+
44
+ export async function setConfigValue(key: string, value: string): Promise<void> {
45
+ await writeConfig({ [key]: value });
46
+ }
47
+
48
+ export async function clearConfigValue(key: string): Promise<void> {
49
+ await writeConfig({ [key]: undefined });
50
+ }
package/src/keyring.ts CHANGED
@@ -4,7 +4,9 @@ const REFRESH_TOKEN_ACCOUNT = 'refresh_token';
4
4
 
5
5
  async function getKeytar(): Promise<typeof import('keytar')> {
6
6
  try {
7
- return await import('keytar');
7
+ const mod = await import('keytar');
8
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- ESM wraps CJS in { default: ... } at runtime despite types
9
+ return mod.default ?? mod;
8
10
  } catch {
9
11
  throw new Error(
10
12
  'keytar is required for credential storage but is not installed. Install it with: npm install keytar'