@mintlify/cli 4.0.1123 → 4.0.1125

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,30 @@
1
+ import { LOCAL_LINKED_CLI_VERSION } from '@mintlify/previewing';
2
+
3
+ import { getCliVersion } from '../src/helpers.js';
4
+
5
+ describe('getCliVersion', () => {
6
+ const originalTestMode = process.env.CLI_TEST_MODE;
7
+
8
+ afterEach(() => {
9
+ if (originalTestMode === undefined) {
10
+ delete process.env.CLI_TEST_MODE;
11
+ } else {
12
+ process.env.CLI_TEST_MODE = originalTestMode;
13
+ }
14
+ });
15
+
16
+ it('returns a test marker when CLI_TEST_MODE is enabled', () => {
17
+ process.env.CLI_TEST_MODE = 'true';
18
+ expect(getCliVersion()).toBe('test-cli');
19
+ });
20
+
21
+ it(
22
+ 'returns LOCAL_LINKED_CLI_VERSION when running from the monorepo source ' +
23
+ '(not inside node_modules)',
24
+ () => {
25
+ // Source lives outside node_modules, mirroring `npm link` / `yarn dev`. See #7121.
26
+ delete process.env.CLI_TEST_MODE;
27
+ expect(getCliVersion()).toBe(LOCAL_LINKED_CLI_VERSION);
28
+ }
29
+ );
30
+ });
package/bin/helpers.js CHANGED
@@ -14,13 +14,14 @@ import { addLog, ErrorLog, getClientVersion, SuccessLog, InfoLog, SpinnerLog, re
14
14
  import { upgradeToDocsConfig, validatePathWithinCwd } from '@mintlify/validation';
15
15
  import detect from 'detect-port';
16
16
  import fse from 'fs-extra';
17
- import fs from 'fs/promises';
18
17
  import inquirer from 'inquirer';
19
18
  import yaml from 'js-yaml';
20
19
  import { exec, execSync } from 'node:child_process';
20
+ import { readFileSync } from 'node:fs';
21
+ import fs from 'node:fs/promises';
22
+ import { fileURLToPath } from 'node:url';
21
23
  import { promisify } from 'node:util';
22
24
  import path from 'path';
23
- import yargs from 'yargs';
24
25
  import { shutdownPostHog } from './telemetry/client.js';
25
26
  export const CMD_EXEC_PATH = process.cwd();
26
27
  export const checkPort = (argv) => __awaiter(void 0, void 0, void 0, function* () {
@@ -109,22 +110,41 @@ export const upgradeConfig = () => __awaiter(void 0, void 0, void 0, function* (
109
110
  addLog(_jsx(ErrorLog, { message: err instanceof Error ? err.message : 'an unknown error occurred' }));
110
111
  }
111
112
  });
112
- export const getCliVersion = () => {
113
- const y = yargs();
114
- let version = undefined;
115
- y.showVersion((s) => {
116
- version = s;
113
+ // Resolve the CLI's own package.json via `import.meta.url` so the version is
114
+ // always the one shipped with this package, regardless of cwd. See #7121.
115
+ const CLI_PACKAGE_JSON_PATH = (() => {
116
+ try {
117
+ return fileURLToPath(new URL('../package.json', import.meta.url));
118
+ }
119
+ catch (_a) {
120
+ return undefined;
121
+ }
122
+ })();
123
+ const readCliPackageVersion = () => {
124
+ if (!CLI_PACKAGE_JSON_PATH)
125
+ return undefined;
126
+ try {
127
+ const pkg = JSON.parse(readFileSync(CLI_PACKAGE_JSON_PATH, 'utf8'));
128
+ return typeof pkg.version === 'string' ? pkg.version : undefined;
129
+ }
130
+ catch (_a) {
131
+ return undefined;
132
+ }
133
+ };
134
+ // Outside `node_modules` means `yarn dev` or `npm link`: treat as linked. See #7121.
135
+ const isRunningFromLinkedBuild = () => {
136
+ if (!CLI_PACKAGE_JSON_PATH)
117
137
  return false;
118
- });
138
+ return !CLI_PACKAGE_JSON_PATH.split(path.sep).includes('node_modules');
139
+ };
140
+ export const getCliVersion = () => {
119
141
  if (process.env.CLI_TEST_MODE === 'true') {
120
142
  return 'test-cli';
121
143
  }
122
- // when running `npm link` the version is 'unknown'
123
- // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
124
- if (version === 'unknown') {
125
- version = LOCAL_LINKED_CLI_VERSION;
144
+ if (isRunningFromLinkedBuild()) {
145
+ return LOCAL_LINKED_CLI_VERSION;
126
146
  }
127
- return version;
147
+ return readCliPackageVersion();
128
148
  };
129
149
  export const getVersions = () => {
130
150
  const cli = getCliVersion();
@@ -9,13 +9,28 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
9
9
  };
10
10
  import { authenticatedFetch } from '../authenticatedFetch.js';
11
11
  import { API_URL } from '../constants.js';
12
- export function getScore(url) {
12
+ export function resolveScoreForUrl(url) {
13
13
  return __awaiter(this, void 0, void 0, function* () {
14
14
  const endpoint = new URL(`${API_URL}/api/cli/score`);
15
15
  endpoint.searchParams.set('url', url);
16
16
  const res = yield authenticatedFetch(endpoint.toString(), {
17
17
  headers: { Accept: 'application/json' },
18
18
  });
19
+ if (res.status !== 200 && res.status !== 202) {
20
+ const body = yield res.text().catch(() => '');
21
+ throw new Error(`API error (${res.status}): ${body || res.statusText}`);
22
+ }
23
+ return res.json();
24
+ });
25
+ }
26
+ export function fetchScoreBySlug(slug) {
27
+ return __awaiter(this, void 0, void 0, function* () {
28
+ const endpoint = new URL(`${API_URL}/api/cli/score/${encodeURIComponent(slug)}`);
29
+ const res = yield authenticatedFetch(endpoint.toString(), {
30
+ headers: { Accept: 'application/json' },
31
+ });
32
+ if (res.status === 202)
33
+ return null;
19
34
  if (!res.ok) {
20
35
  const body = yield res.text().catch(() => '');
21
36
  throw new Error(`API error (${res.status}): ${body || res.statusText}`);
@@ -13,7 +13,9 @@ import chalk from 'chalk';
13
13
  import { Text } from 'ink';
14
14
  import { terminate } from '../helpers.js';
15
15
  import { trackEvent } from '../telemetry/track.js';
16
- import { getScore } from './client.js';
16
+ import { fetchScoreBySlug, resolveScoreForUrl } from './client.js';
17
+ const POLL_INTERVAL_MS = 3000;
18
+ const POLL_TIMEOUT_MS = 120000;
17
19
  function resolveFormat(argv) {
18
20
  if (argv.format === 'table' || argv.format === 'plain' || argv.format === 'json')
19
21
  return argv.format;
@@ -49,6 +51,18 @@ function renderPlain(checks, prefix = '') {
49
51
  }
50
52
  return lines;
51
53
  }
54
+ function renderTable(score, opts) {
55
+ const scoreColor = score.overallScore >= 70 ? 'green' : score.overallScore >= 40 ? 'yellow' : 'red';
56
+ const header = (opts === null || opts === void 0 ? void 0 : opts.stale)
57
+ ? chalk.bold(`\nAgent Readiness Score — ${score.canonicalUrl}`) +
58
+ chalk.dim(` (last run ${new Date(score.computedAt).toLocaleString()})\n`)
59
+ : chalk.bold(`\nAgent Readiness Score — ${score.canonicalUrl}\n`);
60
+ const lines = [header];
61
+ lines.push(` Score: ${chalk[scoreColor].bold(`${score.overallScore}%`)}\n`);
62
+ lines.push(chalk.bold(' Checks'));
63
+ lines.push(...renderChecks(score.checks));
64
+ return lines.join('\n');
65
+ }
52
66
  function output(format, text) {
53
67
  if (format === 'table') {
54
68
  addLog(_jsx(Text, { children: text }));
@@ -57,37 +71,88 @@ function output(format, text) {
57
71
  process.stdout.write(text + '\n');
58
72
  }
59
73
  }
74
+ function sleep(ms) {
75
+ return __awaiter(this, void 0, void 0, function* () {
76
+ return new Promise((resolve) => setTimeout(resolve, ms));
77
+ });
78
+ }
79
+ function pollForScore(slug, afterIso, onAttempt) {
80
+ return __awaiter(this, void 0, void 0, function* () {
81
+ const deadline = Date.now() + POLL_TIMEOUT_MS;
82
+ while (Date.now() < deadline) {
83
+ yield sleep(POLL_INTERVAL_MS);
84
+ onAttempt === null || onAttempt === void 0 ? void 0 : onAttempt();
85
+ const score = yield fetchScoreBySlug(slug);
86
+ if (!score)
87
+ continue;
88
+ if (afterIso === null)
89
+ return score;
90
+ if (new Date(score.computedAt).getTime() > new Date(afterIso).getTime())
91
+ return score;
92
+ }
93
+ throw new Error(`Score did not complete within ${POLL_TIMEOUT_MS / 1000}s`);
94
+ });
95
+ }
60
96
  export const scoreHandler = (argv) => __awaiter(void 0, void 0, void 0, function* () {
61
97
  const format = resolveFormat(argv);
62
98
  try {
63
99
  if (format === 'table')
64
- addLog(_jsx(SpinnerLog, { message: "Running agent readiness checks..." }));
65
- const data = yield getScore(argv.url);
100
+ addLog(_jsx(SpinnerLog, { message: "Checking agent readiness..." }));
101
+ const resolved = yield resolveScoreForUrl(argv.url);
102
+ let final;
103
+ if (resolved.status === 'ready') {
104
+ const score = yield fetchScoreBySlug(resolved.slug);
105
+ if (format === 'table')
106
+ removeLastLog();
107
+ if (!score)
108
+ throw new Error('Score was unexpectedly unavailable');
109
+ final = score;
110
+ }
111
+ else if (resolved.status === 'stale_refresh_queued') {
112
+ const stale = yield fetchScoreBySlug(resolved.slug);
113
+ if (format === 'table')
114
+ removeLastLog();
115
+ if (!stale)
116
+ throw new Error('Stale score was unexpectedly unavailable');
117
+ if (format === 'table') {
118
+ output('table', renderTable(stale, { stale: true }));
119
+ addLog(_jsx(SpinnerLog, { message: "Refreshing score in the background..." }));
120
+ }
121
+ else {
122
+ process.stderr.write('Refreshing score in the background...\n');
123
+ }
124
+ final = yield pollForScore(resolved.slug, stale.computedAt);
125
+ if (format === 'table')
126
+ removeLastLog();
127
+ }
128
+ else {
129
+ if (format === 'table') {
130
+ removeLastLog();
131
+ addLog(_jsx(SpinnerLog, { message: "Queued a new scoring run. Waiting for first result..." }));
132
+ }
133
+ else {
134
+ process.stderr.write('Queued a new scoring run. Waiting for first result...\n');
135
+ }
136
+ final = yield pollForScore(resolved.slug, null);
137
+ if (format === 'table')
138
+ removeLastLog();
139
+ }
66
140
  void trackEvent('cli.score.executed', {
67
141
  url: argv.url,
68
- score: data.overallScore,
142
+ score: final.overallScore,
69
143
  format,
144
+ status: resolved.status,
70
145
  });
71
- if (format === 'table')
72
- removeLastLog();
73
146
  if (format === 'json') {
74
- output(format, JSON.stringify(data, null, 2));
75
- yield terminate(0);
76
- return;
147
+ output(format, JSON.stringify(final, null, 2));
77
148
  }
78
- if (format === 'plain') {
79
- const lines = [`SCORE\t${data.overallScore}`, ...renderPlain(data.checks)];
149
+ else if (format === 'plain') {
150
+ const lines = [`SCORE\t${final.overallScore}`, ...renderPlain(final.checks)];
80
151
  output(format, lines.join('\n'));
81
- yield terminate(0);
82
- return;
83
152
  }
84
- const scoreColor = data.overallScore >= 70 ? 'green' : data.overallScore >= 40 ? 'yellow' : 'red';
85
- const lines = [];
86
- lines.push(chalk.bold(`\nAgent Readiness Score — ${data.url}\n`));
87
- lines.push(` Score: ${chalk[scoreColor].bold(`${data.overallScore}%`)}\n`);
88
- lines.push(chalk.bold(' Checks'));
89
- lines.push(...renderChecks(data.checks));
90
- output(format, lines.join('\n'));
153
+ else {
154
+ output(format, renderTable(final));
155
+ }
91
156
  yield terminate(0);
92
157
  }
93
158
  catch (err) {