@artemiskit/cli 0.1.6 → 0.1.8
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.
- package/CHANGELOG.md +49 -0
- package/dist/index.js +2653 -4832
- package/dist/src/__tests__/helpers/index.d.ts +6 -0
- package/dist/src/__tests__/helpers/index.d.ts.map +1 -0
- package/dist/src/__tests__/helpers/mock-adapter.d.ts +87 -0
- package/dist/src/__tests__/helpers/mock-adapter.d.ts.map +1 -0
- package/dist/src/__tests__/helpers/test-utils.d.ts +47 -0
- package/dist/src/__tests__/helpers/test-utils.d.ts.map +1 -0
- package/dist/src/cli.d.ts.map +1 -1
- package/dist/src/commands/compare.d.ts.map +1 -1
- package/dist/src/commands/history.d.ts.map +1 -1
- package/dist/src/commands/init.d.ts.map +1 -1
- package/dist/src/commands/redteam.d.ts.map +1 -1
- package/dist/src/commands/report.d.ts.map +1 -1
- package/dist/src/commands/run.d.ts.map +1 -1
- package/dist/src/ui/errors.d.ts.map +1 -1
- package/dist/src/ui/panels.d.ts.map +1 -1
- package/dist/src/ui/progress.d.ts.map +1 -1
- package/dist/src/ui/utils.d.ts.map +1 -1
- package/dist/src/utils/update-checker.d.ts +31 -0
- package/dist/src/utils/update-checker.d.ts.map +1 -0
- package/package.json +6 -6
- package/src/__tests__/helpers/mock-adapter.ts +22 -4
- package/src/__tests__/helpers/test-utils.ts +3 -3
- package/src/__tests__/integration/compare-command.test.ts +7 -7
- package/src/__tests__/integration/config.test.ts +2 -2
- package/src/__tests__/integration/history-command.test.ts +2 -2
- package/src/__tests__/integration/init-command.test.ts +3 -3
- package/src/__tests__/integration/report-command.test.ts +2 -2
- package/src/__tests__/integration/ui.test.ts +6 -6
- package/src/cli.ts +22 -1
- package/src/commands/compare.ts +2 -4
- package/src/commands/history.ts +2 -2
- package/src/commands/init.ts +52 -12
- package/src/commands/redteam.ts +6 -6
- package/src/commands/report.ts +3 -3
- package/src/commands/run.ts +4 -4
- package/src/commands/stress.ts +4 -4
- package/src/ui/errors.ts +1 -1
- package/src/ui/live-status.ts +1 -1
- package/src/ui/panels.ts +2 -2
- package/src/ui/progress.ts +1 -1
- package/src/ui/utils.ts +4 -3
- package/src/utils/update-checker.ts +121 -0
package/src/commands/init.ts
CHANGED
|
@@ -3,11 +3,12 @@
|
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
import { existsSync } from 'node:fs';
|
|
6
|
-
import { mkdir, readFile, writeFile
|
|
6
|
+
import { appendFile, mkdir, readFile, writeFile } from 'node:fs/promises';
|
|
7
7
|
import { join } from 'node:path';
|
|
8
8
|
import chalk from 'chalk';
|
|
9
9
|
import { Command } from 'commander';
|
|
10
10
|
import { createSpinner, icons } from '../ui/index.js';
|
|
11
|
+
import { checkForUpdateAndNotify, getCurrentVersion } from '../utils/update-checker.js';
|
|
11
12
|
|
|
12
13
|
const DEFAULT_CONFIG = `# ArtemisKit Configuration
|
|
13
14
|
project: my-project
|
|
@@ -86,18 +87,54 @@ const ENV_KEYS = [
|
|
|
86
87
|
];
|
|
87
88
|
|
|
88
89
|
function renderWelcomeBanner(): string {
|
|
90
|
+
// Brand color for "KIT" portion: #fb923c (orange)
|
|
91
|
+
const brandColor = chalk.hex('#fb923c');
|
|
92
|
+
const version = getCurrentVersion();
|
|
93
|
+
|
|
94
|
+
// Randomly color each border character white or brand color
|
|
95
|
+
const colorBorderChar = (char: string): string => {
|
|
96
|
+
return Math.random() > 0.5 ? chalk.white(char) : brandColor(char);
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
const colorBorder = (str: string): string => {
|
|
100
|
+
return str.split('').map(colorBorderChar).join('');
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
// All lines are exactly 52 chars inside the borders for perfect alignment
|
|
104
|
+
const topBorder = `╭${'─'.repeat(52)}╮`;
|
|
105
|
+
const bottomBorder = `╰${'─'.repeat(52)}╯`;
|
|
106
|
+
const sideBorderLeft = '│';
|
|
107
|
+
const sideBorderRight = '│';
|
|
108
|
+
const emptyContent = ' '.repeat(52);
|
|
109
|
+
|
|
110
|
+
// Version line: "v0.1.7" centered in brand color
|
|
111
|
+
const versionText = `v${version}`;
|
|
112
|
+
const versionPadding = Math.floor((52 - versionText.length) / 2);
|
|
113
|
+
const versionLine =
|
|
114
|
+
' '.repeat(versionPadding) +
|
|
115
|
+
brandColor(versionText) +
|
|
116
|
+
' '.repeat(52 - versionPadding - versionText.length);
|
|
117
|
+
|
|
118
|
+
// Tagline centered
|
|
119
|
+
const tagline = 'Open-source testing toolkit for LLM applications';
|
|
120
|
+
const taglinePadding = Math.floor((52 - tagline.length) / 2);
|
|
121
|
+
const taglineLine =
|
|
122
|
+
' '.repeat(taglinePadding) +
|
|
123
|
+
chalk.gray(tagline) +
|
|
124
|
+
' '.repeat(52 - taglinePadding - tagline.length);
|
|
125
|
+
|
|
89
126
|
const lines = [
|
|
90
127
|
'',
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
chalk.
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
128
|
+
` ${colorBorder(topBorder)}`,
|
|
129
|
+
` ${colorBorderChar(sideBorderLeft)}${emptyContent}${colorBorderChar(sideBorderRight)}`,
|
|
130
|
+
` ${colorBorderChar(sideBorderLeft)} ${chalk.bold.white('▄▀█ █▀█ ▀█▀ █▀▀ █▀▄▀█ █ █▀ ')}${brandColor.bold('█▄▀ █ ▀█▀')} ${colorBorderChar(sideBorderRight)}`,
|
|
131
|
+
` ${colorBorderChar(sideBorderLeft)} ${chalk.bold.white('█▀█ █▀▄ █ ██▄ █ ▀ █ █ ▄█ ')}${brandColor.bold('█ █ █ █ ')} ${colorBorderChar(sideBorderRight)}`,
|
|
132
|
+
` ${colorBorderChar(sideBorderLeft)}${emptyContent}${colorBorderChar(sideBorderRight)}`,
|
|
133
|
+
` ${colorBorderChar(sideBorderLeft)}${versionLine}${colorBorderChar(sideBorderRight)}`,
|
|
134
|
+
` ${colorBorderChar(sideBorderLeft)}${emptyContent}${colorBorderChar(sideBorderRight)}`,
|
|
135
|
+
` ${colorBorderChar(sideBorderLeft)}${taglineLine}${colorBorderChar(sideBorderRight)}`,
|
|
136
|
+
` ${colorBorderChar(sideBorderLeft)}${emptyContent}${colorBorderChar(sideBorderRight)}`,
|
|
137
|
+
` ${colorBorder(bottomBorder)}`,
|
|
101
138
|
'',
|
|
102
139
|
];
|
|
103
140
|
return lines.join('\n');
|
|
@@ -180,7 +217,7 @@ async function appendEnvKeys(cwd: string): Promise<{ added: string[]; skipped: s
|
|
|
180
217
|
// Add newline before our content if file exists and doesn't end with newline
|
|
181
218
|
const prefix =
|
|
182
219
|
existingContent && !existingContent.endsWith('\n') ? '\n\n' : existingContent ? '\n' : '';
|
|
183
|
-
await appendFile(envPath, prefix + linesToAdd.join('\n')
|
|
220
|
+
await appendFile(envPath, `${prefix + linesToAdd.join('\n')}\n`);
|
|
184
221
|
}
|
|
185
222
|
|
|
186
223
|
return { added, skipped };
|
|
@@ -260,6 +297,9 @@ export function initCommand(): Command {
|
|
|
260
297
|
|
|
261
298
|
// Show success panel
|
|
262
299
|
console.log(renderSuccessPanel());
|
|
300
|
+
|
|
301
|
+
// Non-blocking update check (fire and forget)
|
|
302
|
+
checkForUpdateAndNotify();
|
|
263
303
|
} catch (error) {
|
|
264
304
|
spinner.fail('Error');
|
|
265
305
|
console.error(chalk.red(`\n${icons.failed} ${(error as Error).message}`));
|
package/src/commands/redteam.ts
CHANGED
|
@@ -35,13 +35,13 @@ import { nanoid } from 'nanoid';
|
|
|
35
35
|
import { loadConfig } from '../config/loader.js';
|
|
36
36
|
import {
|
|
37
37
|
createSpinner,
|
|
38
|
-
|
|
38
|
+
getProviderErrorContext,
|
|
39
|
+
icons,
|
|
40
|
+
isTTY,
|
|
39
41
|
renderError,
|
|
40
42
|
renderInfoBox,
|
|
41
43
|
renderProgressBar,
|
|
42
|
-
|
|
43
|
-
isTTY,
|
|
44
|
-
icons,
|
|
44
|
+
renderRedteamSummaryPanel,
|
|
45
45
|
} from '../ui/index.js';
|
|
46
46
|
import {
|
|
47
47
|
buildAdapterConfig,
|
|
@@ -213,7 +213,7 @@ export function redteamCommand(): Command {
|
|
|
213
213
|
|
|
214
214
|
// Clear progress line
|
|
215
215
|
if (isTTY) {
|
|
216
|
-
process.stdout.write(
|
|
216
|
+
process.stdout.write(`\r${' '.repeat(60)}\r`);
|
|
217
217
|
}
|
|
218
218
|
|
|
219
219
|
// Display status with appropriate icon
|
|
@@ -267,7 +267,7 @@ export function redteamCommand(): Command {
|
|
|
267
267
|
|
|
268
268
|
// Clear progress line
|
|
269
269
|
if (isTTY) {
|
|
270
|
-
process.stdout.write(
|
|
270
|
+
process.stdout.write(`\r${' '.repeat(60)}\r`);
|
|
271
271
|
}
|
|
272
272
|
|
|
273
273
|
// Apply redaction to prompt even for errors/blocked
|
package/src/commands/report.ts
CHANGED
|
@@ -13,7 +13,7 @@ import {
|
|
|
13
13
|
} from '@artemiskit/reports';
|
|
14
14
|
import { Command } from 'commander';
|
|
15
15
|
import { loadConfig } from '../config/loader.js';
|
|
16
|
-
import { createSpinner, renderError, renderInfoBox
|
|
16
|
+
import { createSpinner, icons, renderError, renderInfoBox } from '../ui/index.js';
|
|
17
17
|
import { createStorage } from '../utils/storage.js';
|
|
18
18
|
|
|
19
19
|
interface ReportOptions {
|
|
@@ -96,7 +96,7 @@ export function reportCommand(): Command {
|
|
|
96
96
|
const htmlPath = join(outputDir, `${runId}.html`);
|
|
97
97
|
await writeFile(htmlPath, html);
|
|
98
98
|
generatedFiles.push(htmlPath);
|
|
99
|
-
spinner.succeed(
|
|
99
|
+
spinner.succeed('Generated HTML report');
|
|
100
100
|
}
|
|
101
101
|
|
|
102
102
|
if (format === 'json' || format === 'both') {
|
|
@@ -105,7 +105,7 @@ export function reportCommand(): Command {
|
|
|
105
105
|
const jsonPath = join(outputDir, `${runId}.json`);
|
|
106
106
|
await writeFile(jsonPath, json);
|
|
107
107
|
generatedFiles.push(jsonPath);
|
|
108
|
-
spinner.succeed(
|
|
108
|
+
spinner.succeed('Generated JSON report');
|
|
109
109
|
}
|
|
110
110
|
|
|
111
111
|
// Show success panel
|
package/src/commands/run.ts
CHANGED
|
@@ -13,14 +13,14 @@ import { Command } from 'commander';
|
|
|
13
13
|
import { loadConfig } from '../config/loader.js';
|
|
14
14
|
import {
|
|
15
15
|
createSpinner,
|
|
16
|
+
formatDuration,
|
|
17
|
+
getProviderErrorContext,
|
|
16
18
|
icons,
|
|
19
|
+
isTTY,
|
|
20
|
+
padText,
|
|
17
21
|
renderError,
|
|
18
22
|
renderProgressBar,
|
|
19
23
|
renderSummaryPanel,
|
|
20
|
-
getProviderErrorContext,
|
|
21
|
-
formatDuration,
|
|
22
|
-
padText,
|
|
23
|
-
isTTY,
|
|
24
24
|
} from '../ui/index.js';
|
|
25
25
|
import {
|
|
26
26
|
buildAdapterConfig,
|
package/src/commands/stress.ts
CHANGED
|
@@ -21,14 +21,14 @@ import { Command } from 'commander';
|
|
|
21
21
|
import { nanoid } from 'nanoid';
|
|
22
22
|
import { loadConfig } from '../config/loader.js';
|
|
23
23
|
import {
|
|
24
|
+
colors,
|
|
24
25
|
createSpinner,
|
|
25
|
-
|
|
26
|
+
getProviderErrorContext,
|
|
27
|
+
isTTY,
|
|
26
28
|
renderError,
|
|
27
29
|
renderInfoBox,
|
|
28
30
|
renderProgressBar,
|
|
29
|
-
|
|
30
|
-
isTTY,
|
|
31
|
-
colors,
|
|
31
|
+
renderStressSummaryPanel,
|
|
32
32
|
} from '../ui/index.js';
|
|
33
33
|
import {
|
|
34
34
|
buildAdapterConfig,
|
package/src/ui/errors.ts
CHANGED
|
@@ -148,7 +148,7 @@ export function renderWarning(title: string, message: string, suggestions?: stri
|
|
|
148
148
|
currentLine += (currentLine.length > 2 ? ' ' : '') + word;
|
|
149
149
|
} else {
|
|
150
150
|
lines.push(chalk.yellow('│') + padText(currentLine, width - 2) + chalk.yellow('│'));
|
|
151
|
-
currentLine =
|
|
151
|
+
currentLine = ` ${word}`;
|
|
152
152
|
}
|
|
153
153
|
}
|
|
154
154
|
if (currentLine.length > 2) {
|
package/src/ui/live-status.ts
CHANGED
|
@@ -4,9 +4,9 @@
|
|
|
4
4
|
|
|
5
5
|
import chalk from 'chalk';
|
|
6
6
|
import ora, { type Ora } from 'ora';
|
|
7
|
-
import { isTTY } from './utils.js';
|
|
8
7
|
import { icons } from './colors.js';
|
|
9
8
|
import { renderProgressBar } from './progress.js';
|
|
9
|
+
import { isTTY } from './utils.js';
|
|
10
10
|
|
|
11
11
|
export type TestStatus = 'pending' | 'running' | 'passed' | 'failed' | 'skipped';
|
|
12
12
|
|
package/src/ui/panels.ts
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
import chalk from 'chalk';
|
|
6
6
|
import { formatPercentage, icons } from './colors.js';
|
|
7
|
-
import {
|
|
7
|
+
import { centerText, formatDuration, isTTY, padText } from './utils.js';
|
|
8
8
|
|
|
9
9
|
export interface SummaryData {
|
|
10
10
|
passed: number;
|
|
@@ -180,7 +180,7 @@ export function renderRedteamSummaryPanel(data: RedteamSummaryData): string {
|
|
|
180
180
|
chalk.magenta('║'),
|
|
181
181
|
chalk.magenta(`╠${border}╣`),
|
|
182
182
|
chalk.magenta('║') +
|
|
183
|
-
padText(` Defense Rate: ${defenseColor(data.defenseRate.toFixed(1)
|
|
183
|
+
padText(` Defense Rate: ${defenseColor(`${data.defenseRate.toFixed(1)}%`)}`, width - 2) +
|
|
184
184
|
chalk.magenta('║'),
|
|
185
185
|
chalk.magenta(`╚${border}╝`),
|
|
186
186
|
];
|
package/src/ui/progress.ts
CHANGED
|
@@ -108,7 +108,7 @@ export class ProgressBar {
|
|
|
108
108
|
if (isTTY) {
|
|
109
109
|
// Clear previous line and write new output
|
|
110
110
|
const clearLength = stripAnsi(this.lastOutput).length;
|
|
111
|
-
process.stdout.write(
|
|
111
|
+
process.stdout.write(`\r${' '.repeat(clearLength)}\r`);
|
|
112
112
|
process.stdout.write(output);
|
|
113
113
|
this.lastOutput = output;
|
|
114
114
|
}
|
package/src/ui/utils.ts
CHANGED
|
@@ -46,7 +46,8 @@ export function padText(
|
|
|
46
46
|
|
|
47
47
|
if (align === 'center') {
|
|
48
48
|
return centerText(text, width);
|
|
49
|
-
}
|
|
49
|
+
}
|
|
50
|
+
if (align === 'right') {
|
|
50
51
|
return ' '.repeat(paddingNeeded) + text;
|
|
51
52
|
}
|
|
52
53
|
return text + ' '.repeat(paddingNeeded);
|
|
@@ -56,7 +57,7 @@ export function padText(
|
|
|
56
57
|
* Strip ANSI escape codes from string (for length calculations)
|
|
57
58
|
*/
|
|
58
59
|
export function stripAnsi(str: string): string {
|
|
59
|
-
//
|
|
60
|
+
// biome-ignore lint/suspicious/noControlCharactersInRegex: ANSI escape codes require control characters
|
|
60
61
|
return str.replace(/\x1B\[[0-9;]*[a-zA-Z]/g, '');
|
|
61
62
|
}
|
|
62
63
|
|
|
@@ -66,7 +67,7 @@ export function stripAnsi(str: string): string {
|
|
|
66
67
|
export function truncate(text: string, maxWidth: number): string {
|
|
67
68
|
const visibleLength = stripAnsi(text).length;
|
|
68
69
|
if (visibleLength <= maxWidth) return text;
|
|
69
|
-
return text.slice(0, maxWidth - 1)
|
|
70
|
+
return `${text.slice(0, maxWidth - 1)}…`;
|
|
70
71
|
}
|
|
71
72
|
|
|
72
73
|
/**
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Non-blocking update checker for ArtemisKit CLI
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import chalk from 'chalk';
|
|
6
|
+
import { version as currentVersion } from '../../package.json';
|
|
7
|
+
|
|
8
|
+
const PACKAGE_NAME = '@artemiskit/cli';
|
|
9
|
+
const NPM_REGISTRY_URL = `https://registry.npmjs.org/${PACKAGE_NAME}/latest`;
|
|
10
|
+
const FETCH_TIMEOUT_MS = 3000; // 3 second timeout to avoid blocking
|
|
11
|
+
|
|
12
|
+
// Brand color
|
|
13
|
+
const brandColor = chalk.hex('#fb923c');
|
|
14
|
+
|
|
15
|
+
export interface UpdateInfo {
|
|
16
|
+
currentVersion: string;
|
|
17
|
+
latestVersion: string;
|
|
18
|
+
updateAvailable: boolean;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Fetches the latest version from npm registry with a timeout
|
|
23
|
+
*/
|
|
24
|
+
async function fetchLatestVersion(): Promise<string | null> {
|
|
25
|
+
const controller = new AbortController();
|
|
26
|
+
const timeoutId = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS);
|
|
27
|
+
|
|
28
|
+
try {
|
|
29
|
+
const response = await fetch(NPM_REGISTRY_URL, {
|
|
30
|
+
signal: controller.signal,
|
|
31
|
+
headers: {
|
|
32
|
+
Accept: 'application/json',
|
|
33
|
+
},
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
if (!response.ok) {
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const data = (await response.json()) as { version?: string };
|
|
41
|
+
return data.version || null;
|
|
42
|
+
} catch {
|
|
43
|
+
// Silently fail - network issues shouldn't block CLI usage
|
|
44
|
+
return null;
|
|
45
|
+
} finally {
|
|
46
|
+
clearTimeout(timeoutId);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Compares two semver versions
|
|
52
|
+
* Returns true if latest > current
|
|
53
|
+
*/
|
|
54
|
+
function isNewerVersion(current: string, latest: string): boolean {
|
|
55
|
+
const currentParts = current.replace(/^v/, '').split('.').map(Number);
|
|
56
|
+
const latestParts = latest.replace(/^v/, '').split('.').map(Number);
|
|
57
|
+
|
|
58
|
+
for (let i = 0; i < 3; i++) {
|
|
59
|
+
const c = currentParts[i] || 0;
|
|
60
|
+
const l = latestParts[i] || 0;
|
|
61
|
+
if (l > c) return true;
|
|
62
|
+
if (l < c) return false;
|
|
63
|
+
}
|
|
64
|
+
return false;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Check for updates (non-blocking)
|
|
69
|
+
* Returns update info or null if check fails
|
|
70
|
+
*/
|
|
71
|
+
export async function checkForUpdate(): Promise<UpdateInfo | null> {
|
|
72
|
+
const latestVersion = await fetchLatestVersion();
|
|
73
|
+
|
|
74
|
+
if (!latestVersion) {
|
|
75
|
+
return null;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
return {
|
|
79
|
+
currentVersion,
|
|
80
|
+
latestVersion,
|
|
81
|
+
updateAvailable: isNewerVersion(currentVersion, latestVersion),
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Get the current CLI version
|
|
87
|
+
*/
|
|
88
|
+
export function getCurrentVersion(): string {
|
|
89
|
+
return currentVersion;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Format version display string
|
|
94
|
+
*/
|
|
95
|
+
export function formatVersionDisplay(version: string): string {
|
|
96
|
+
return `${chalk.bold('ArtemisKit CLI')} ${brandColor(`v${version}`)}`;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Format update available message
|
|
101
|
+
*/
|
|
102
|
+
export function formatUpdateMessage(current: string, latest: string): string {
|
|
103
|
+
return `\n${chalk.yellow('╭─────────────────────────────────────────────────────╮')}\n${chalk.yellow('│')}${chalk.yellow(' Update available! ')}${chalk.gray(`${current}`)}${chalk.yellow(' → ')}${brandColor.bold(`${latest}`)}${' '.repeat(24 - current.length - latest.length)}${chalk.yellow('│')}\n${chalk.yellow('│')}${chalk.white(' Run ')}${chalk.cyan('npm install -g @artemiskit/cli')}${chalk.white(' to update ')}${chalk.yellow('│')}\n${chalk.yellow('╰─────────────────────────────────────────────────────╯')}`;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Non-blocking update check that prints message if update available
|
|
108
|
+
* Use this to fire-and-forget an update check
|
|
109
|
+
*/
|
|
110
|
+
export function checkForUpdateAndNotify(): void {
|
|
111
|
+
// Fire and forget - don't await
|
|
112
|
+
checkForUpdate()
|
|
113
|
+
.then((info) => {
|
|
114
|
+
if (info?.updateAvailable) {
|
|
115
|
+
console.log(formatUpdateMessage(info.currentVersion, info.latestVersion));
|
|
116
|
+
}
|
|
117
|
+
})
|
|
118
|
+
.catch(() => {
|
|
119
|
+
// Silently ignore errors
|
|
120
|
+
});
|
|
121
|
+
}
|