@mintlify/cli 4.0.1094 → 4.0.1096

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.
@@ -93,9 +93,12 @@ describe('resolveFormat', () => {
93
93
  });
94
94
 
95
95
  it('returns specified format', () => {
96
+ const prev = process.env.CLAUDECODE;
97
+ delete process.env.CLAUDECODE;
96
98
  expect(resolveFormat({ format: 'plain' })).toBe('plain');
97
99
  expect(resolveFormat({ format: 'json' })).toBe('json');
98
100
  expect(resolveFormat({ format: 'graph' })).toBe('graph');
101
+ process.env.CLAUDECODE = prev;
99
102
  });
100
103
 
101
104
  it('defaults to plain', () => {
@@ -29,6 +29,8 @@ vi.mock('fs-extra', () => ({
29
29
 
30
30
  vi.mock('../src/helpers.js', () => ({
31
31
  CMD_EXEC_PATH: '/fake/project',
32
+ isAI: () =>
33
+ !process.stdin.isTTY || process.env.CLAUDECODE === '1' || process.env.TERM_PROGRAM === 'claude',
32
34
  }));
33
35
 
34
36
  const addLogSpy = vi.mocked(previewing.addLog);
package/bin/helpers.js CHANGED
@@ -174,6 +174,9 @@ export const terminate = (code) => __awaiter(void 0, void 0, void 0, function* (
174
174
  process.exit(code);
175
175
  });
176
176
  export const execAsync = promisify(exec);
177
+ export function isAI() {
178
+ return (!process.stdin.isTTY || process.env.CLAUDECODE === '1' || process.env.TERM_PROGRAM === 'claude');
179
+ }
177
180
  export const detectPackageManager = (_a) => __awaiter(void 0, [_a], void 0, function* ({ packageName }) {
178
181
  try {
179
182
  const { stdout: packagePath } = yield execAsync(`which ${packageName}`);
package/bin/init.js CHANGED
@@ -14,6 +14,7 @@ import { docsConfigSchema, validatePathWithinCwd } from '@mintlify/validation';
14
14
  import AdmZip from 'adm-zip';
15
15
  import fse from 'fs-extra';
16
16
  import { Box, Text } from 'ink';
17
+ import { isAI } from './helpers.js';
17
18
  const sendOnboardingMessage = (installDir) => {
18
19
  addLogs(_jsx(Text, { bold: true, children: "Documentation Setup!" }), _jsx(Text, { children: "To see your docs run" }), _jsxs(Box, { children: [_jsx(Text, { color: "blue", children: "cd" }), _jsxs(Text, { children: [" ", installDir] })] }), _jsx(Text, { color: "blue", children: "mint dev" }));
19
20
  };
@@ -24,9 +25,6 @@ export function init(installDir, force, theme, name) {
24
25
  return __awaiter(this, void 0, void 0, function* () {
25
26
  // Validate path is within current working directory to prevent path traversal
26
27
  validatePathWithinCwd(installDir);
27
- const isInteractive = process.stdin.isTTY;
28
- const isClaudeCode = process.env.CLAUDECODE === '1';
29
- const isAI = !isInteractive || isClaudeCode;
30
28
  let selectedTheme = theme;
31
29
  let projectName = name;
32
30
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -35,15 +33,15 @@ export function init(installDir, force, theme, name) {
35
33
  });
36
34
  const dirContents = yield fse.readdir(installDir).catch(() => []);
37
35
  const contentsOccupied = dirContents.length > 0;
38
- if ((!theme || !name) && isAI) {
36
+ if ((!theme || !name) && isAI()) {
39
37
  sendUsageMessageForAI(installDir, contentsOccupied, themes);
40
38
  return;
41
39
  }
42
- if (contentsOccupied && isAI && !force) {
40
+ if (contentsOccupied && isAI() && !force) {
43
41
  sendUsageMessageForAI(installDir, contentsOccupied, themes);
44
42
  return;
45
43
  }
46
- if (contentsOccupied && !isAI) {
44
+ if (contentsOccupied && !isAI()) {
47
45
  const choice = yield select({
48
46
  message: `Directory ${installDir} is not empty. What would you like to do?`,
49
47
  choices: [
@@ -68,7 +66,7 @@ export function init(installDir, force, theme, name) {
68
66
  validatePathWithinCwd(installDir, process.cwd());
69
67
  }
70
68
  }
71
- if (!isAI && (!selectedTheme || !projectName)) {
69
+ if (!isAI() && (!selectedTheme || !projectName)) {
72
70
  const defaultProject = projectName !== undefined ? projectName : installDir === '.' ? 'Mintlify' : installDir;
73
71
  if (!projectName) {
74
72
  projectName = yield input({
package/bin/login.js CHANGED
@@ -17,6 +17,7 @@ import { calculatePKCECodeChallenge, randomNonce, randomPKCECodeVerifier, random
17
17
  import { startCallbackServer } from './callbackServer.js';
18
18
  import { DASHBOARD_URL, STYTCH_CLIENT_ID, TOKEN_ENDPOINT } from './constants.js';
19
19
  import { storeCredentials } from './keyring.js';
20
+ import { trackLoginAttempt, trackLoginFailed, trackLoginSuccess } from './telemetry/track.js';
20
21
  export function login() {
21
22
  return __awaiter(this, void 0, void 0, function* () {
22
23
  var _a, _b;
@@ -29,6 +30,7 @@ export function login() {
29
30
  authorizeUrl.searchParams.set('state', state);
30
31
  authorizeUrl.searchParams.set('code_challenge', codeChallenge);
31
32
  const url = authorizeUrl.toString();
33
+ void trackLoginAttempt();
32
34
  const { codePromise, close: closeServer } = yield startCallbackServer();
33
35
  addLog(_jsxs(Box, { flexDirection: "column", gap: 1, paddingY: 1, children: [_jsxs(Text, { bold: true, children: [_jsx(Text, { color: "green", children: "\u25C6 " }), "A browser window will open for Mintlify authentication"] }), _jsxs(Box, { flexDirection: "column", paddingLeft: 3, gap: 1, children: [_jsx(Text, { dimColor: true, children: "If your browser doesn't open automatically, copy this URL:" }), _jsx(Text, { color: "cyan", children: url })] })] }));
34
36
  open(url).catch(() => { });
@@ -69,11 +71,14 @@ export function login() {
69
71
  });
70
72
  const body = yield res.json().catch(() => ({}));
71
73
  if (!res.ok) {
72
- addLog(_jsxs(Text, { color: "red", children: ["\u2716 Login failed: ", (_b = (_a = body.error_message) !== null && _a !== void 0 ? _a : body.error) !== null && _b !== void 0 ? _b : 'unknown error'] }));
74
+ const reason = (_b = (_a = body.error_message) !== null && _a !== void 0 ? _a : body.error) !== null && _b !== void 0 ? _b : 'unknown error';
75
+ void trackLoginFailed(reason);
76
+ addLog(_jsxs(Text, { color: "red", children: ["\u2716 Login failed: ", reason] }));
73
77
  return;
74
78
  }
75
79
  const token = body;
76
80
  yield storeCredentials(token.access_token, token.refresh_token);
81
+ void trackLoginSuccess();
77
82
  addLog(_jsx(Text, { color: "green", children: "\u2714 Logged in successfully." }));
78
83
  });
79
84
  }
@@ -7,6 +7,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
7
7
  step((generator = generator.apply(thisArg, _arguments || [])).next());
8
8
  });
9
9
  };
10
+ import { getConfigValue } from '../config.js';
10
11
  import { getVersions } from '../helpers.js';
11
12
  import { trackCommand } from './track.js';
12
13
  const SCRAPE_SUBCOMMANDS = new Set(['page', 'site', 'openapi']);
@@ -32,7 +33,8 @@ export function createTelemetryMiddleware() {
32
33
  tracked = true;
33
34
  const sanitizedCommand = getSanitizedCommandForTelemetry(argv._);
34
35
  const { cli: cliVersion } = getVersions();
35
- void trackCommand({ command: sanitizedCommand, cliVersion });
36
+ const subdomain = getConfigValue('subdomain');
37
+ void trackCommand({ command: sanitizedCommand, cliVersion, subdomain });
36
38
  }
37
39
  });
38
40
  };
@@ -10,7 +10,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
10
10
  import os from 'os';
11
11
  import { isTelemetryEnabled } from '../config.js';
12
12
  import { TELEMETRY_ASYNC_TIMEOUT_MS } from '../constants.js';
13
- import { getVersions } from '../helpers.js';
13
+ import { getVersions, isAI } from '../helpers.js';
14
14
  import { getPostHogClient } from './client.js';
15
15
  import { getDistinctId } from './distinctId.js';
16
16
  function captureWithTimeout(event, properties) {
@@ -31,7 +31,7 @@ function captureWithTimeout(event, properties) {
31
31
  });
32
32
  }
33
33
  export function trackCommand(_a) {
34
- return __awaiter(this, arguments, void 0, function* ({ command, cliVersion }) {
34
+ return __awaiter(this, arguments, void 0, function* ({ command, cliVersion, subdomain, }) {
35
35
  if (!isTelemetryEnabled())
36
36
  return;
37
37
  try {
@@ -41,11 +41,39 @@ export function trackCommand(_a) {
41
41
  os: os.platform(),
42
42
  arch: os.arch(),
43
43
  node_version: process.version,
44
+ is_ai_agent: isAI(),
45
+ subdomain,
44
46
  });
45
47
  }
46
48
  catch (_b) { }
47
49
  });
48
50
  }
51
+ function trackLoginEvent(event, extra) {
52
+ return __awaiter(this, void 0, void 0, function* () {
53
+ if (!isTelemetryEnabled())
54
+ return;
55
+ try {
56
+ const { cli: cliVersion } = getVersions();
57
+ yield captureWithTimeout(event, Object.assign(Object.assign({}, extra), { cli_version: cliVersion, os: os.platform(), arch: os.arch(), node_version: process.version }));
58
+ }
59
+ catch (_a) { }
60
+ });
61
+ }
62
+ export function trackLoginAttempt() {
63
+ return __awaiter(this, void 0, void 0, function* () {
64
+ return trackLoginEvent('cli.login.attempted');
65
+ });
66
+ }
67
+ export function trackLoginSuccess() {
68
+ return __awaiter(this, void 0, void 0, function* () {
69
+ return trackLoginEvent('cli.login.succeeded');
70
+ });
71
+ }
72
+ export function trackLoginFailed(reason) {
73
+ return __awaiter(this, void 0, void 0, function* () {
74
+ return trackLoginEvent('cli.login.failed', { reason });
75
+ });
76
+ }
49
77
  export function trackTelemetryPreferenceChange(options) {
50
78
  return __awaiter(this, void 0, void 0, function* () {
51
79
  if (process.env.CLI_TEST_MODE === 'true')